From 95bb7a0b9a59cc8c818bfeeeec5e6d16ea30512f Mon Sep 17 00:00:00 2001 From: Wil Wilsman Date: Wed, 5 Oct 2022 13:54:33 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Merge=20deferred=20snapshot=20resou?= =?UTF-8?q?rces=20by=20widths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/snapshot.js | 40 +++++++++++++++++++++++++++++++++-- packages/core/src/utils.js | 3 ++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index 77b5698fd..b6e5f54b2 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -230,6 +230,40 @@ export async function* gatherSnapshots(options, context) { return snapshots; } +// Merges snapshots and deduplicates resource arrays. Duplicate log resources are replaced, root +// resources are deduplicated by widths, and all other resources are deduplicated by their URL. +function mergeSnapshotOptions(prev = {}, next) { + let { resources: oldResources = [], ...existing } = prev; + let { resources: newResources = [], widths = [], width, ...incoming } = next; + + // prioritize singular widths over mutilple widths + widths = width ? [width] : widths; + + // deduplicate resources by associated widths and url + let resources = oldResources.reduce((all, resource) => { + if (resource.log || resource.widths.every(w => widths.includes(w))) return all; + if (!resource.root && all.some(r => r.url === resource.url)) return all; + resource.widths = resource.widths.filter(w => !widths.includes(w)); + return all.concat(resource); + }, newResources.map(r => ({ ...r, widths }))); + + // sort resources after merging; roots first by min-width & logs last + resources.sort((a, b) => { + if (a.root && b.root) return Math.min(...b.widths) - Math.min(...a.widths); + return (a.root || b.log) ? -1 : (a.log || b.root) ? 1 : 0; + }); + + // overwrite resources and ensure unique widths + return PercyConfig.merge([ + existing, incoming, { widths, resources } + ], (path, prev, next) => { + if (path[0] === 'resources') return [path, next]; + if (path[0] === 'widths' && prev && next) { + return [path, [...new Set([...prev, ...next])]]; + } + }); +} + // Creates a snapshots queue that manages a Percy build and uploads snapshots. export function createSnapshotsQueue(percy) { let { concurrency } = percy.config.discovery; @@ -274,7 +308,7 @@ export function createSnapshotsQueue(percy) { .handle('find', ({ name }, snapshot) => ( snapshot.name === name )) - // when pushed, maybe flush old snapshots + // when pushed, maybe flush old snapshots or possibly merge with existing snapshots .handle('push', (snapshot, existing) => { let { name, meta } = snapshot; @@ -284,7 +318,9 @@ export function createSnapshotsQueue(percy) { // immediately flush when uploads are delayed but not skipped if (percy.delayUploads && !percy.skipUploads) queue.flush(); - return snapshot; + + // merge snapshot options when uploads are deferred + return percy.deferUploads ? mergeSnapshotOptions(existing, snapshot) : snapshot; }) // send snapshots to be uploaded to the build .handle('task', async function*({ resources, ...snapshot }) { diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 722aaca98..1eb9fddc7 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -43,7 +43,8 @@ export function createPercyCSSResource(url, css) { // Creates a log resource object. export function createLogResource(logs) { - return createResource(`/percy.${Date.now()}.log`, JSON.stringify(logs), 'text/plain'); + let [url, content] = [`/percy.${Date.now()}.log`, JSON.stringify(logs)]; + return createResource(url, content, 'text/plain', { log: true }); } // Returns true or false if the provided object is a generator or not