From 0100dce84b8395331da963da0c2f2d1e1eeccc3a Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 26 Jul 2022 20:31:58 -0600 Subject: [PATCH] fix: improve size handling, reduced manifest footprint --- packages/next/build/utils.ts | 128 ++++++++----- .../shared/lib/router/utils/sorted-routes.js | 170 ------------------ .../lib/router/utils/sorted-routes.js.map | 1 - 3 files changed, 84 insertions(+), 215 deletions(-) delete mode 100644 packages/next/shared/lib/router/utils/sorted-routes.js delete mode 100644 packages/next/shared/lib/router/utils/sorted-routes.js.map diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 3004ce4a941f..222fb58e9ed2 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -155,7 +155,7 @@ export async function printTreeView( const messages: [string, string, string][] = [] - const sizes = await computeFromManifest( + const stats = await computeFromManifest( { build: buildManifest, app: appBuildManifest }, distPath, gzipSize, @@ -164,14 +164,11 @@ export async function printTreeView( const printFileTree = async ({ list, - size, routerType, }: { list: ReadonlyArray - size: ComputeFilesManifest routerType: ROUTER_TYPE }) => { - messages.push([routerType === 'app' ? 'App' : 'Pages', '', '']) messages.push( [routerType === 'app' ? 'Route' : 'Page', 'Size', 'First Load JS'].map( (entry) => chalk.underline(entry) @@ -237,7 +234,9 @@ export async function printTreeView( const uniqueCssFiles = buildManifest.pages[item]?.filter( - (file) => file.endsWith('.css') && size.unique.files.includes(file) + (file) => + file.endsWith('.css') && + stats.router[routerType]?.unique.files.includes(file) ) || [] if (uniqueCssFiles.length > 0) { @@ -245,11 +244,10 @@ export async function printTreeView( uniqueCssFiles.forEach((file, index, { length }) => { const innerSymbol = index === length - 1 ? '└' : '├' + const size = stats.sizes.get(file) messages.push([ `${contSymbol} ${innerSymbol} ${getCleanName(file)}`, - typeof size.unique.size.files[file] === 'number' - ? prettyBytes(size.unique.size.files[file]) - : '', + typeof size === 'number' ? prettyBytes(size) : '', '', ]) }) @@ -323,13 +321,17 @@ export async function printTreeView( } }) - const sharedFilesSize = size.common.size.total - const sharedFiles = size.common.size.files + const sharedFilesSize = stats.router[routerType]?.common.size.total + const sharedFiles = stats.router[routerType]?.common.files ?? [] - messages.push(['+ First Load JS', getPrettySize(sharedFilesSize), '']) + messages.push([ + '+ First Load JS', + typeof sharedFilesSize === 'number' ? getPrettySize(sharedFilesSize) : '', + '', + ]) const sharedCssFiles: string[] = [] ;[ - ...Object.keys(sharedFiles) + ...sharedFiles .filter((file) => { if (file.endsWith('.css')) { sharedCssFiles.push(file) @@ -345,20 +347,22 @@ export async function printTreeView( const originalName = fileName.replace('', buildId) const cleanName = getCleanName(fileName) + const size = stats.sizes.get(originalName) messages.push([ ` ${innerSymbol} ${cleanName}`, - prettyBytes(sharedFiles[originalName]), + typeof size === 'number' ? prettyBytes(size) : '', '', ]) }) } - if (lists.app && sizes.app) { + // If enabled, then print the tree for the app directory. + if (lists.app && stats.router.app) { + messages.push(['App', '', '']) await printFileTree({ routerType: 'app', list: lists.app, - size: sizes.app, }) messages.push(['', '', '']) @@ -370,14 +374,17 @@ export async function printTreeView( } as any) if (!lists.pages.includes('/404')) { + if (lists.app && stats.router.app) { + messages.push(['Pages', '', '']) + } + lists.pages = [...lists.pages, '/404'] } - // Print the tree view for the app directory. + // Print the tree view for the pages directory. await printFileTree({ routerType: 'pages', list: lists.pages, - size: sizes.pages, }) const middlewareInfo = middlewareManifest.middleware?.['/'] @@ -522,7 +529,6 @@ export function printCustomRoutes({ type ComputeFilesGroup = { files: ReadonlyArray size: { - files: Record total: number } } @@ -533,8 +539,11 @@ type ComputeFilesManifest = { } type ComputeFilesManifestResult = { - pages: ComputeFilesManifest - app?: ComputeFilesManifest + router: { + pages: ComputeFilesManifest + app?: ComputeFilesManifest + } + sizes: Map } let cachedBuildManifest: BuildManifest | undefined @@ -642,21 +651,25 @@ export async function computeFromManifest( }): Promise => { const entries = [...listing.each.entries()] - const shapeGroup = (group: [string, number][]): ComputeFilesGroup => ({ - files: group.map(([f]) => f), - size: group.reduce( + const shapeGroup = (group: [string, number][]): ComputeFilesGroup => + group.reduce( (acc, [f]) => { - // TODO: (wyattjoh) see if we can remove this - const size = stats.get(f)! + acc.files.push(f) - acc.files[f] = size - acc.total += size + const size = stats.get(f) + if (typeof size === 'number') { + acc.size.total += size + } return acc }, - { files: {}, total: 0 } - ), - }) + { + files: [] as string[], + size: { + total: 0, + }, + } + ) return { unique: shapeGroup(entries.filter(([, len]) => len === 1)), @@ -669,8 +682,11 @@ export async function computeFromManifest( } lastCompute = { - pages: await groupFiles(files.pages), - app: files.app ? await groupFiles(files.app) : undefined, + router: { + pages: await groupFiles(files.pages), + app: files.app ? await groupFiles(files.app) : undefined, + }, + sizes: stats, } cachedBuildManifest = manifests.build @@ -679,6 +695,10 @@ export async function computeFromManifest( return lastCompute! } +export function unique(main: ReadonlyArray, sub: ReadonlyArray): T[] { + return [...new Set([...main, ...sub])] +} + export function difference( main: ReadonlyArray | ReadonlySet, sub: ReadonlyArray | ReadonlySet @@ -688,6 +708,9 @@ export function difference( return [...a].filter((x) => !b.has(x)) } +/** + * Return an array of the items shared by both arrays. + */ function intersect(main: ReadonlyArray, sub: ReadonlyArray): T[] { const a = new Set(main) const b = new Set(sub) @@ -709,23 +732,25 @@ export async function getJsPageSizeInKb( buildManifest: BuildManifest, appBuildManifest?: AppBuildManifest, gzipSize: boolean = true, - computedManifestData?: ComputeFilesManifestResult + cachedStats?: ComputeFilesManifestResult ): Promise<[number, number]> { const pageManifest = routerType === 'pages' ? buildManifest : appBuildManifest if (!pageManifest) { throw new Error('expected appBuildManifest with an "app" pageType') } - const data = - computedManifestData ?? + // If stats was not provided, then compute it again. + const stats = + cachedStats ?? (await computeFromManifest( { build: buildManifest, app: appBuildManifest }, distPath, gzipSize )) - const pageData = routerType === 'pages' ? data.pages : data.app + const pageData = stats.router[routerType] if (!pageData) { + // This error shouldn't happen and represents an error in Next.js. throw new Error('expected "app" manifest data with an "app" pageType') } @@ -741,26 +766,41 @@ export async function getJsPageSizeInKb( const fnMapRealPath = (dep: string) => `${distPath}/${dep}` - const allFilesReal = [...new Set([...pageFiles, ...appFiles])].map( - fnMapRealPath - ) + const allFilesReal = unique(pageFiles, appFiles).map(fnMapRealPath) const selfFilesReal = difference( + // Find the files shared by the pages files and the unique files... intersect(pageFiles, pageData.unique.files), + // but without the common files. pageData.common.files ).map(fnMapRealPath) - // TODO: (wyattjoh) see if we can reuse the manifest sizes - const getSize = gzipSize ? fsStatGzip : fsStat + // Try to get the file size from the page data if available, otherwise do a + // raw compute. + const getCachedSize = async (file: string) => { + const key = file.slice(distPath.length + 1) + const size: number | undefined = stats.sizes.get(key) + + // If the size wasn't in the stats bundle, then get it from the file + // directly. + if (typeof size !== 'number') { + return getSize(file) + } + + return size + } + try { // Doesn't use `Promise.all`, as we'd double compute duplicate files. This // function is memoized, so the second one will instantly resolve. - const allFilesSize = sum(await Promise.all(allFilesReal.map(getSize))) - const selfFilesSize = sum(await Promise.all(selfFilesReal.map(getSize))) + const allFilesSize = sum(await Promise.all(allFilesReal.map(getCachedSize))) + const selfFilesSize = sum( + await Promise.all(selfFilesReal.map(getCachedSize)) + ) return [selfFilesSize, allFilesSize] - } catch (_) {} + } catch {} return [-1, -1] } diff --git a/packages/next/shared/lib/router/utils/sorted-routes.js b/packages/next/shared/lib/router/utils/sorted-routes.js deleted file mode 100644 index f79a0f765a37..000000000000 --- a/packages/next/shared/lib/router/utils/sorted-routes.js +++ /dev/null @@ -1,170 +0,0 @@ -'use strict'; -Object.defineProperty(exports, '__esModule', { - value: true -}); -exports.getSortedRoutes = getSortedRoutes; -class UrlNode { - insert(urlPath) { - this._insert(urlPath.split('/').filter(Boolean), [], false); - } - smoosh() { - return this._smoosh(); - } - _smoosh(prefix = '/') { - const childrenPaths = [ - ...this.children.keys() - ].sort(); - if (this.slugName !== null) { - childrenPaths.splice(childrenPaths.indexOf('[]'), 1); - } - if (this.restSlugName !== null) { - childrenPaths.splice(childrenPaths.indexOf('[...]'), 1); - } - if (this.optionalRestSlugName !== null) { - childrenPaths.splice(childrenPaths.indexOf('[[...]]'), 1); - } - const routes = childrenPaths.map((c)=>this.children.get(c)._smoosh(`${prefix}${c}/`)).reduce((prev, curr)=>[ - ...prev, - ...curr - ], []); - if (this.slugName !== null) { - routes.push(...this.children.get('[]')._smoosh(`${prefix}[${this.slugName}]/`)); - } - if (!this.placeholder) { - const r = prefix === '/' ? '/' : prefix.slice(0, -1); - if (this.optionalRestSlugName != null) { - throw new Error(`You cannot define a route with the same specificity as a optional catch-all route ("${r}" and "${r}[[...${this.optionalRestSlugName}]]").`); - } - routes.unshift(r); - } - if (this.restSlugName !== null) { - routes.push(...this.children.get('[...]')._smoosh(`${prefix}[...${this.restSlugName}]/`)); - } - if (this.optionalRestSlugName !== null) { - routes.push(...this.children.get('[[...]]')._smoosh(`${prefix}[[...${this.optionalRestSlugName}]]/`)); - } - return routes; - } - _insert(urlPaths, slugNames, isCatchAll) { - if (urlPaths.length === 0) { - this.placeholder = false; - return; - } - if (isCatchAll) { - throw new Error(`Catch-all must be the last part of the URL.`); - } - // The next segment in the urlPaths list - let nextSegment = urlPaths[0]; - // Check if the segment matches `[something]` - if (nextSegment.startsWith('[') && nextSegment.endsWith(']')) { - // Strip `[` and `]`, leaving only `something` - let segmentName = nextSegment.slice(1, -1); - let isOptional = false; - if (segmentName.startsWith('[') && segmentName.endsWith(']')) { - // Strip optional `[` and `]`, leaving only `something` - segmentName = segmentName.slice(1, -1); - isOptional = true; - } - if (segmentName.startsWith('...')) { - // Strip `...`, leaving only `something` - segmentName = segmentName.substring(3); - isCatchAll = true; - } - if (segmentName.startsWith('[') || segmentName.endsWith(']')) { - throw new Error(`Segment names may not start or end with extra brackets ('${segmentName}').`); - } - if (segmentName.startsWith('.')) { - throw new Error(`Segment names may not start with erroneous periods ('${segmentName}').`); - } - function handleSlug(previousSlug, nextSlug) { - if (previousSlug !== null) { - // If the specific segment already has a slug but the slug is not `something` - // This prevents collisions like: - // pages/[post]/index.js - // pages/[id]/index.js - // Because currently multiple dynamic params on the same segment level are not supported - if (previousSlug !== nextSlug) { - // TODO: This error seems to be confusing for users, needs an error link, the description can be based on above comment. - throw new Error(`You cannot use different slug names for the same dynamic path ('${previousSlug}' !== '${nextSlug}').`); - } - } - slugNames.forEach((slug)=>{ - if (slug === nextSlug) { - throw new Error(`You cannot have the same slug name "${nextSlug}" repeat within a single dynamic path`); - } - if (slug.replace(/\W/g, '') === nextSegment.replace(/\W/g, '')) { - throw new Error(`You cannot have the slug names "${slug}" and "${nextSlug}" differ only by non-word symbols within a single dynamic path`); - } - }); - slugNames.push(nextSlug); - } - if (isCatchAll) { - if (isOptional) { - if (this.restSlugName != null) { - throw new Error(`You cannot use both an required and optional catch-all route at the same level ("[...${this.restSlugName}]" and "${urlPaths[0]}" ).`); - } - handleSlug(this.optionalRestSlugName, segmentName); - // slugName is kept as it can only be one particular slugName - this.optionalRestSlugName = segmentName; - // nextSegment is overwritten to [[...]] so that it can later be sorted specifically - nextSegment = '[[...]]'; - } else { - if (this.optionalRestSlugName != null) { - throw new Error(`You cannot use both an optional and required catch-all route at the same level ("[[...${this.optionalRestSlugName}]]" and "${urlPaths[0]}").`); - } - handleSlug(this.restSlugName, segmentName); - // slugName is kept as it can only be one particular slugName - this.restSlugName = segmentName; - // nextSegment is overwritten to [...] so that it can later be sorted specifically - nextSegment = '[...]'; - } - } else { - if (isOptional) { - throw new Error(`Optional route parameters are not yet supported ("${urlPaths[0]}").`); - } - handleSlug(this.slugName, segmentName); - // slugName is kept as it can only be one particular slugName - this.slugName = segmentName; - // nextSegment is overwritten to [] so that it can later be sorted specifically - nextSegment = '[]'; - } - } - // If this UrlNode doesn't have the nextSegment yet we create a new child UrlNode - if (!this.children.has(nextSegment)) { - this.children.set(nextSegment, new UrlNode()); - } - this.children.get(nextSegment)._insert(urlPaths.slice(1), slugNames, isCatchAll); - } - constructor(){ - this.placeholder = true; - this.children = new Map(); - this.slugName = null; - this.restSlugName = null; - this.optionalRestSlugName = null; - } -} -function getSortedRoutes(normalizedPages) { - // First the UrlNode is created, and every UrlNode can have only 1 dynamic segment - // Eg you can't have pages/[post]/abc.js and pages/[hello]/something-else.js - // Only 1 dynamic segment per nesting level - // So in the case that is test/integration/dynamic-routing it'll be this: - // pages/[post]/comments.js - // pages/blog/[post]/comment/[id].js - // Both are fine because `pages/[post]` and `pages/blog` are on the same level - // So in this case `UrlNode` created here has `this.slugName === 'post'` - // And since your PR passed through `slugName` as an array basically it'd including it in too many possibilities - // Instead what has to be passed through is the upwards path's dynamic names - const root = new UrlNode(); - // Here the `root` gets injected multiple paths, and insert will break them up into sublevels - normalizedPages.forEach((pagePath)=>root.insert(pagePath)); - // Smoosh will then sort those sublevels up to the point where you get the correct route definition priority - return root.smoosh(); -} //# sourceMappingURL=sorted-routes.js.map - -if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') { - Object.defineProperty(exports.default, '__esModule', { value: true }); - Object.assign(exports.default, exports); - module.exports = exports.default; -} - -//# sourceMappingURL=sorted-routes.js.map \ No newline at end of file diff --git a/packages/next/shared/lib/router/utils/sorted-routes.js.map b/packages/next/shared/lib/router/utils/sorted-routes.js.map deleted file mode 100644 index c464cdabbeff..000000000000 --- a/packages/next/shared/lib/router/utils/sorted-routes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../../../../shared/lib/router/utils/sorted-routes.ts"],"names":["getSortedRoutes","UrlNode","placeholder","children","Map","slugName","restSlugName","optionalRestSlugName","insert","urlPath","_insert","split","filter","Boolean","smoosh","_smoosh","prefix","childrenPaths","keys","sort","splice","indexOf","routes","map","c","get","reduce","prev","curr","push","r","slice","Error","unshift","urlPaths","slugNames","isCatchAll","length","nextSegment","startsWith","endsWith","segmentName","isOptional","substring","handleSlug","previousSlug","nextSlug","forEach","slug","replace","has","set","normalizedPages","root","pagePath"],"mappings":"AAAA;;;;QAqMgBA,eAAe,GAAfA,eAAe;AArM/B,MAAMC,OAAO;IACXC,WAAW,GAAY,IAAI,CAAA;IAC3BC,QAAQ,GAAyB,IAAIC,GAAG,EAAE,CAAA;IAC1CC,QAAQ,GAAkB,IAAI,CAAA;IAC9BC,YAAY,GAAkB,IAAI,CAAA;IAClCC,oBAAoB,GAAkB,IAAI,CAAA;IAE1CC,MAAM,CAACC,OAAe,EAAQ;QAC5B,IAAI,CAACC,OAAO,CAACD,OAAO,CAACE,KAAK,CAAC,GAAG,CAAC,CAACC,MAAM,CAACC,OAAO,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC;KAC5D;IAEDC,MAAM,GAAa;QACjB,OAAO,IAAI,CAACC,OAAO,EAAE,CAAA;KACtB;IAED,AAAQA,OAAO,CAACC,MAAc,GAAG,GAAG,EAAY;QAC9C,MAAMC,aAAa,GAAG;eAAI,IAAI,CAACd,QAAQ,CAACe,IAAI,EAAE;SAAC,CAACC,IAAI,EAAE;QACtD,IAAI,IAAI,CAACd,QAAQ,KAAK,IAAI,EAAE;YAC1BY,aAAa,CAACG,MAAM,CAACH,aAAa,CAACI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACrD;QACD,IAAI,IAAI,CAACf,YAAY,KAAK,IAAI,EAAE;YAC9BW,aAAa,CAACG,MAAM,CAACH,aAAa,CAACI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACxD;QACD,IAAI,IAAI,CAACd,oBAAoB,KAAK,IAAI,EAAE;YACtCU,aAAa,CAACG,MAAM,CAACH,aAAa,CAACI,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;SAC1D;QAED,MAAMC,MAAM,GAAGL,aAAa,CACzBM,GAAG,CAAC,CAACC,CAAC,GAAK,IAAI,CAACrB,QAAQ,CAACsB,GAAG,CAACD,CAAC,CAAC,CAAET,OAAO,CAAC,CAAC,EAAEC,MAAM,CAAC,EAAEQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC3DE,MAAM,CAAC,CAACC,IAAI,EAAEC,IAAI,GAAK;mBAAID,IAAI;mBAAKC,IAAI;aAAC,EAAE,EAAE,CAAC;QAEjD,IAAI,IAAI,CAACvB,QAAQ,KAAK,IAAI,EAAE;YAC1BiB,MAAM,CAACO,IAAI,IACN,IAAI,CAAC1B,QAAQ,CAACsB,GAAG,CAAC,IAAI,CAAC,CAAEV,OAAO,CAAC,CAAC,EAAEC,MAAM,CAAC,CAAC,EAAE,IAAI,CAACX,QAAQ,CAAC,EAAE,CAAC,CAAC,CACpE;SACF;QAED,IAAI,CAAC,IAAI,CAACH,WAAW,EAAE;YACrB,MAAM4B,CAAC,GAAGd,MAAM,KAAK,GAAG,GAAG,GAAG,GAAGA,MAAM,CAACe,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACpD,IAAI,IAAI,CAACxB,oBAAoB,IAAI,IAAI,EAAE;gBACrC,MAAM,IAAIyB,KAAK,CACb,CAAC,oFAAoF,EAAEF,CAAC,CAAC,OAAO,EAAEA,CAAC,CAAC,KAAK,EAAE,IAAI,CAACvB,oBAAoB,CAAC,KAAK,CAAC,CAC5I,CAAA;aACF;YAEDe,MAAM,CAACW,OAAO,CAACH,CAAC,CAAC;SAClB;QAED,IAAI,IAAI,CAACxB,YAAY,KAAK,IAAI,EAAE;YAC9BgB,MAAM,CAACO,IAAI,IACN,IAAI,CAAC1B,QAAQ,CACbsB,GAAG,CAAC,OAAO,CAAC,CACZV,OAAO,CAAC,CAAC,EAAEC,MAAM,CAAC,IAAI,EAAE,IAAI,CAACV,YAAY,CAAC,EAAE,CAAC,CAAC,CAClD;SACF;QAED,IAAI,IAAI,CAACC,oBAAoB,KAAK,IAAI,EAAE;YACtCe,MAAM,CAACO,IAAI,IACN,IAAI,CAAC1B,QAAQ,CACbsB,GAAG,CAAC,SAAS,CAAC,CACdV,OAAO,CAAC,CAAC,EAAEC,MAAM,CAAC,KAAK,EAAE,IAAI,CAACT,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAC5D;SACF;QAED,OAAOe,MAAM,CAAA;KACd;IAED,AAAQZ,OAAO,CACbwB,QAAkB,EAClBC,SAAmB,EACnBC,UAAmB,EACb;QACN,IAAIF,QAAQ,CAACG,MAAM,KAAK,CAAC,EAAE;YACzB,IAAI,CAACnC,WAAW,GAAG,KAAK;YACxB,OAAM;SACP;QAED,IAAIkC,UAAU,EAAE;YACd,MAAM,IAAIJ,KAAK,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAA;SAC/D;QAED,wCAAwC;QACxC,IAAIM,WAAW,GAAGJ,QAAQ,CAAC,CAAC,CAAC;QAE7B,6CAA6C;QAC7C,IAAII,WAAW,CAACC,UAAU,CAAC,GAAG,CAAC,IAAID,WAAW,CAACE,QAAQ,CAAC,GAAG,CAAC,EAAE;YAC5D,8CAA8C;YAC9C,IAAIC,WAAW,GAAGH,WAAW,CAACP,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAE1C,IAAIW,UAAU,GAAG,KAAK;YACtB,IAAID,WAAW,CAACF,UAAU,CAAC,GAAG,CAAC,IAAIE,WAAW,CAACD,QAAQ,CAAC,GAAG,CAAC,EAAE;gBAC5D,uDAAuD;gBACvDC,WAAW,GAAGA,WAAW,CAACV,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtCW,UAAU,GAAG,IAAI;aAClB;YAED,IAAID,WAAW,CAACF,UAAU,CAAC,KAAK,CAAC,EAAE;gBACjC,wCAAwC;gBACxCE,WAAW,GAAGA,WAAW,CAACE,SAAS,CAAC,CAAC,CAAC;gBACtCP,UAAU,GAAG,IAAI;aAClB;YAED,IAAIK,WAAW,CAACF,UAAU,CAAC,GAAG,CAAC,IAAIE,WAAW,CAACD,QAAQ,CAAC,GAAG,CAAC,EAAE;gBAC5D,MAAM,IAAIR,KAAK,CACb,CAAC,yDAAyD,EAAES,WAAW,CAAC,GAAG,CAAC,CAC7E,CAAA;aACF;YAED,IAAIA,WAAW,CAACF,UAAU,CAAC,GAAG,CAAC,EAAE;gBAC/B,MAAM,IAAIP,KAAK,CACb,CAAC,qDAAqD,EAAES,WAAW,CAAC,GAAG,CAAC,CACzE,CAAA;aACF;YAED,SAASG,UAAU,CAACC,YAA2B,EAAEC,QAAgB,EAAE;gBACjE,IAAID,YAAY,KAAK,IAAI,EAAE;oBACzB,6EAA6E;oBAC7E,iCAAiC;oBACjC,wBAAwB;oBACxB,sBAAsB;oBACtB,wFAAwF;oBACxF,IAAIA,YAAY,KAAKC,QAAQ,EAAE;wBAC7B,wHAAwH;wBACxH,MAAM,IAAId,KAAK,CACb,CAAC,gEAAgE,EAAEa,YAAY,CAAC,OAAO,EAAEC,QAAQ,CAAC,GAAG,CAAC,CACvG,CAAA;qBACF;iBACF;gBAEDX,SAAS,CAACY,OAAO,CAAC,CAACC,IAAI,GAAK;oBAC1B,IAAIA,IAAI,KAAKF,QAAQ,EAAE;wBACrB,MAAM,IAAId,KAAK,CACb,CAAC,oCAAoC,EAAEc,QAAQ,CAAC,qCAAqC,CAAC,CACvF,CAAA;qBACF;oBAED,IAAIE,IAAI,CAACC,OAAO,QAAQ,EAAE,CAAC,KAAKX,WAAW,CAACW,OAAO,QAAQ,EAAE,CAAC,EAAE;wBAC9D,MAAM,IAAIjB,KAAK,CACb,CAAC,gCAAgC,EAAEgB,IAAI,CAAC,OAAO,EAAEF,QAAQ,CAAC,8DAA8D,CAAC,CAC1H,CAAA;qBACF;iBACF,CAAC;gBAEFX,SAAS,CAACN,IAAI,CAACiB,QAAQ,CAAC;aACzB;YAED,IAAIV,UAAU,EAAE;gBACd,IAAIM,UAAU,EAAE;oBACd,IAAI,IAAI,CAACpC,YAAY,IAAI,IAAI,EAAE;wBAC7B,MAAM,IAAI0B,KAAK,CACb,CAAC,qFAAqF,EAAE,IAAI,CAAC1B,YAAY,CAAC,QAAQ,EAAE4B,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CACtI,CAAA;qBACF;oBAEDU,UAAU,CAAC,IAAI,CAACrC,oBAAoB,EAAEkC,WAAW,CAAC;oBAClD,6DAA6D;oBAC7D,IAAI,CAAClC,oBAAoB,GAAGkC,WAAW;oBACvC,oFAAoF;oBACpFH,WAAW,GAAG,SAAS;iBACxB,MAAM;oBACL,IAAI,IAAI,CAAC/B,oBAAoB,IAAI,IAAI,EAAE;wBACrC,MAAM,IAAIyB,KAAK,CACb,CAAC,sFAAsF,EAAE,IAAI,CAACzB,oBAAoB,CAAC,SAAS,EAAE2B,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAC/I,CAAA;qBACF;oBAEDU,UAAU,CAAC,IAAI,CAACtC,YAAY,EAAEmC,WAAW,CAAC;oBAC1C,6DAA6D;oBAC7D,IAAI,CAACnC,YAAY,GAAGmC,WAAW;oBAC/B,kFAAkF;oBAClFH,WAAW,GAAG,OAAO;iBACtB;aACF,MAAM;gBACL,IAAII,UAAU,EAAE;oBACd,MAAM,IAAIV,KAAK,CACb,CAAC,kDAAkD,EAAEE,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CACtE,CAAA;iBACF;gBACDU,UAAU,CAAC,IAAI,CAACvC,QAAQ,EAAEoC,WAAW,CAAC;gBACtC,6DAA6D;gBAC7D,IAAI,CAACpC,QAAQ,GAAGoC,WAAW;gBAC3B,+EAA+E;gBAC/EH,WAAW,GAAG,IAAI;aACnB;SACF;QAED,iFAAiF;QACjF,IAAI,CAAC,IAAI,CAACnC,QAAQ,CAAC+C,GAAG,CAACZ,WAAW,CAAC,EAAE;YACnC,IAAI,CAACnC,QAAQ,CAACgD,GAAG,CAACb,WAAW,EAAE,IAAIrC,OAAO,EAAE,CAAC;SAC9C;QAED,IAAI,CAACE,QAAQ,CACVsB,GAAG,CAACa,WAAW,CAAC,CAChB5B,OAAO,CAACwB,QAAQ,CAACH,KAAK,CAAC,CAAC,CAAC,EAAEI,SAAS,EAAEC,UAAU,CAAC;KACrD;CACF;AAEM,SAASpC,eAAe,CAACoD,eAAyB,EAAY;IACnE,kFAAkF;IAClF,4EAA4E;IAC5E,2CAA2C;IAE3C,yEAAyE;IACzE,2BAA2B;IAC3B,oCAAoC;IACpC,8EAA8E;IAC9E,wEAAwE;IACxE,gHAAgH;IAChH,4EAA4E;IAC5E,MAAMC,IAAI,GAAG,IAAIpD,OAAO,EAAE;IAE1B,6FAA6F;IAC7FmD,eAAe,CAACL,OAAO,CAAC,CAACO,QAAQ,GAAKD,IAAI,CAAC7C,MAAM,CAAC8C,QAAQ,CAAC,CAAC;IAC5D,4GAA4G;IAC5G,OAAOD,IAAI,CAACvC,MAAM,EAAE,CAAA;CACrB"} \ No newline at end of file