From 003c66ae807a5141c69d07006a3d2d65fbeebc9c Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:52:24 -0400 Subject: [PATCH 01/10] add failing test --- packages/vite/src/__tests__/main.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index 20439c96..661cf7a0 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -502,8 +502,8 @@ describe('vite-imagetools', () => { }) describe('cache.dir', () => { + const dir = './node_modules/.cache/imagetools_test_cache_dir' test('is used', async () => { - const dir = './node_modules/.cache/imagetools_test_cache_dir' await rm(dir, { recursive: true, force: true }) const root = join(__dirname, '__fixtures__') await build({ @@ -522,6 +522,12 @@ describe('vite-imagetools', () => { const image = (await readdir(dir))[0] expect(image).toBeTypeOf('string') }) + + test('is consistent', async () => { + const image = (await readdir(dir))[0] + + expect(image).toBe('3884ad0a2b86439bb0abda8c08ee68e9fbee7c43') + }) }) describe('cache.avifFormat', () => { From 2791ba228f48afd948271e5fe9cbdbde277bdebd Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 1 Apr 2024 12:57:17 -0400 Subject: [PATCH 02/10] shouldn't depend on `mtime` --- packages/vite/src/__tests__/main.test.ts | 2 +- packages/vite/src/utils.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index 661cf7a0..25ccef25 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -526,7 +526,7 @@ describe('vite-imagetools', () => { test('is consistent', async () => { const image = (await readdir(dir))[0] - expect(image).toBe('3884ad0a2b86439bb0abda8c08ee68e9fbee7c43') + expect(image).toBe('e88de4c0f4a1a99e1674825722c51afca1bdc51f') }) }) diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index b53eefa1..f2688776 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -1,6 +1,5 @@ import { createHash } from 'node:crypto' import path from 'node:path' -import { statSync } from 'node:fs' import type { ImageConfig } from 'imagetools-core' export const createBasePath = (base?: string) => { @@ -16,8 +15,7 @@ export async function generateImageID(url: URL, config: ImageConfig, imageBuffer // baseURL isn't a valid URL, but just a string used for an identifier // use a relative path in the local case so that it's consistent across machines const baseURL = new URL(url.protocol + path.relative(process.cwd(), url.pathname)) - const { mtime } = statSync(path.resolve(process.cwd(), decodeURIComponent(url.pathname))) - return hash([baseURL.href, JSON.stringify(config), mtime.getTime().toString()]) + return hash([baseURL.href, JSON.stringify(config)]) } function hash(keyParts: Array) { From 4e28950f80254bc167ebe83b9ff5e94bc86a4444 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:28:49 -0400 Subject: [PATCH 03/10] changeset --- .changeset/breezy-melons-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/breezy-melons-wave.md diff --git a/.changeset/breezy-melons-wave.md b/.changeset/breezy-melons-wave.md new file mode 100644 index 00000000..4fab4d93 --- /dev/null +++ b/.changeset/breezy-melons-wave.md @@ -0,0 +1,5 @@ +--- +'vite-imagetools': patch +--- + +fix: consistent image id hashes across machines From 86b6e0e25885c45b211727109872fd74bf6e9c8d Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:56:35 -0400 Subject: [PATCH 04/10] replace `mtime` stat for `size` --- packages/vite/src/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index f2688776..bc618e87 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -1,5 +1,6 @@ import { createHash } from 'node:crypto' import path from 'node:path' +import { statSync } from 'node:fs' import type { ImageConfig } from 'imagetools-core' export const createBasePath = (base?: string) => { @@ -15,7 +16,8 @@ export async function generateImageID(url: URL, config: ImageConfig, imageBuffer // baseURL isn't a valid URL, but just a string used for an identifier // use a relative path in the local case so that it's consistent across machines const baseURL = new URL(url.protocol + path.relative(process.cwd(), url.pathname)) - return hash([baseURL.href, JSON.stringify(config)]) + const { size } = statSync(path.resolve(process.cwd(), decodeURIComponent(url.pathname))) + return hash([baseURL.href, JSON.stringify(config), size.toString()]) } function hash(keyParts: Array) { From d572214bbda069cfe799b9f6731d6e574c153a5b Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:57:32 -0400 Subject: [PATCH 05/10] update test --- packages/vite/src/__tests__/main.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index 25ccef25..9cc93964 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -526,7 +526,7 @@ describe('vite-imagetools', () => { test('is consistent', async () => { const image = (await readdir(dir))[0] - expect(image).toBe('e88de4c0f4a1a99e1674825722c51afca1bdc51f') + expect(image).toBe('b4ddbd3e7ccbad6ec1ecb43ec83523e7f3cfe3b0') }) }) From 57f390af3cf9ee9791612b48e9b80c6b4ab53be3 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:57:46 -0400 Subject: [PATCH 06/10] hash with the image instead --- packages/vite/src/__tests__/main.test.ts | 2 +- packages/vite/src/utils.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index 9cc93964..b0a32e77 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -526,7 +526,7 @@ describe('vite-imagetools', () => { test('is consistent', async () => { const image = (await readdir(dir))[0] - expect(image).toBe('b4ddbd3e7ccbad6ec1ecb43ec83523e7f3cfe3b0') + expect(image).toBe('32832663b21d26da61d880b4909edd37a9dbd853') }) }) diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index bc618e87..65b51927 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -1,6 +1,5 @@ import { createHash } from 'node:crypto' import path from 'node:path' -import { statSync } from 'node:fs' import type { ImageConfig } from 'imagetools-core' export const createBasePath = (base?: string) => { @@ -16,8 +15,7 @@ export async function generateImageID(url: URL, config: ImageConfig, imageBuffer // baseURL isn't a valid URL, but just a string used for an identifier // use a relative path in the local case so that it's consistent across machines const baseURL = new URL(url.protocol + path.relative(process.cwd(), url.pathname)) - const { size } = statSync(path.resolve(process.cwd(), decodeURIComponent(url.pathname))) - return hash([baseURL.href, JSON.stringify(config), size.toString()]) + return hash([baseURL.href, JSON.stringify(config), imageBuffer]) } function hash(keyParts: Array) { From a4aec62469b9b98b320f3d0de6f21541a1315a27 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:43:47 -0400 Subject: [PATCH 07/10] remove async --- packages/vite/src/index.ts | 2 +- packages/vite/src/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index f5df3028..532e2145 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -136,7 +136,7 @@ export function imagetools(userOptions: Partial = {}): Plugin const imageBuffer = await img.clone().toBuffer() for (const config of imageConfigs) { - const id = await generateImageID(srcURL, config, imageBuffer) + const id = generateImageID(srcURL, config, imageBuffer) let image: Sharp | undefined let metadata: ImageMetadata diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index 65b51927..98f2b539 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -6,7 +6,7 @@ export const createBasePath = (base?: string) => { return (base?.replace(/\/$/, '') || '') + '/@imagetools/' } -export async function generateImageID(url: URL, config: ImageConfig, imageBuffer: Buffer) { +export function generateImageID(url: URL, config: ImageConfig, imageBuffer: Buffer) { if (url.host) { const baseURL = new URL(url.origin + url.pathname) return hash([baseURL.href, JSON.stringify(config), imageBuffer]) From 9a64cfdfa851f186b2c402bcec085e9fec5f58e9 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:14:02 -0400 Subject: [PATCH 08/10] hash the image buffer once --- packages/vite/src/__tests__/main.test.ts | 2 +- packages/vite/src/index.ts | 5 +++-- packages/vite/src/utils.ts | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index b0a32e77..8851cdad 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -526,7 +526,7 @@ describe('vite-imagetools', () => { test('is consistent', async () => { const image = (await readdir(dir))[0] - expect(image).toBe('32832663b21d26da61d880b4909edd37a9dbd853') + expect(image).toBe('7a7bca50a264376853d5c77aba0bddc0163c02f9') }) }) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 532e2145..e0e4bac6 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -20,7 +20,7 @@ import { } from 'imagetools-core' import { createFilter, dataToEsm } from '@rollup/pluginutils' import sharp, { type Metadata, type Sharp } from 'sharp' -import { createBasePath, generateImageID } from './utils.js' +import { createBasePath, generateImageID, hash } from './utils.js' import type { VitePluginOptions } from './types.js' export type { @@ -136,7 +136,8 @@ export function imagetools(userOptions: Partial = {}): Plugin const imageBuffer = await img.clone().toBuffer() for (const config of imageConfigs) { - const id = generateImageID(srcURL, config, imageBuffer) + const imageHash = hash([imageBuffer]) + const id = generateImageID(srcURL, config, imageHash) let image: Sharp | undefined let metadata: ImageMetadata diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index 98f2b539..4a0fbfc1 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -6,19 +6,19 @@ export const createBasePath = (base?: string) => { return (base?.replace(/\/$/, '') || '') + '/@imagetools/' } -export function generateImageID(url: URL, config: ImageConfig, imageBuffer: Buffer) { +export function generateImageID(url: URL, config: ImageConfig, imageHash: string) { if (url.host) { const baseURL = new URL(url.origin + url.pathname) - return hash([baseURL.href, JSON.stringify(config), imageBuffer]) + return hash([baseURL.href, JSON.stringify(config), imageHash]) } // baseURL isn't a valid URL, but just a string used for an identifier // use a relative path in the local case so that it's consistent across machines const baseURL = new URL(url.protocol + path.relative(process.cwd(), url.pathname)) - return hash([baseURL.href, JSON.stringify(config), imageBuffer]) + return hash([baseURL.href, JSON.stringify(config), imageHash]) } -function hash(keyParts: Array) { +export function hash(keyParts: Array) { let hash = createHash('sha1') for (const keyPart of keyParts) { hash = hash.update(keyPart) From edda6c7e74a46d4c352c818822f80d6430ca870e Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:14:35 -0400 Subject: [PATCH 09/10] oops, move it up --- packages/vite/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index e0e4bac6..2c2b9bac 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -135,8 +135,8 @@ export function imagetools(userOptions: Partial = {}): Plugin const imageBuffer = await img.clone().toBuffer() + const imageHash = hash([imageBuffer]) for (const config of imageConfigs) { - const imageHash = hash([imageBuffer]) const id = generateImageID(srcURL, config, imageHash) let image: Sharp | undefined let metadata: ImageMetadata From e43d13f31e1f98e95db02af1f6f045d678117d77 Mon Sep 17 00:00:00 2001 From: AdrianGonz97 <31664583+AdrianGonz97@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:37:34 -0400 Subject: [PATCH 10/10] remove `url` from hash --- packages/vite/src/__tests__/main.test.ts | 2 +- packages/vite/src/index.ts | 2 +- packages/vite/src/utils.ts | 13 ++----------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/vite/src/__tests__/main.test.ts b/packages/vite/src/__tests__/main.test.ts index 8851cdad..e225585c 100644 --- a/packages/vite/src/__tests__/main.test.ts +++ b/packages/vite/src/__tests__/main.test.ts @@ -526,7 +526,7 @@ describe('vite-imagetools', () => { test('is consistent', async () => { const image = (await readdir(dir))[0] - expect(image).toBe('7a7bca50a264376853d5c77aba0bddc0163c02f9') + expect(image).toBe('325b80fade286c672ea884b87e65f7a3278a9f8a') }) }) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 2c2b9bac..f033f115 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -137,7 +137,7 @@ export function imagetools(userOptions: Partial = {}): Plugin const imageHash = hash([imageBuffer]) for (const config of imageConfigs) { - const id = generateImageID(srcURL, config, imageHash) + const id = generateImageID(config, imageHash) let image: Sharp | undefined let metadata: ImageMetadata diff --git a/packages/vite/src/utils.ts b/packages/vite/src/utils.ts index 4a0fbfc1..9cc5da16 100644 --- a/packages/vite/src/utils.ts +++ b/packages/vite/src/utils.ts @@ -1,21 +1,12 @@ import { createHash } from 'node:crypto' -import path from 'node:path' import type { ImageConfig } from 'imagetools-core' export const createBasePath = (base?: string) => { return (base?.replace(/\/$/, '') || '') + '/@imagetools/' } -export function generateImageID(url: URL, config: ImageConfig, imageHash: string) { - if (url.host) { - const baseURL = new URL(url.origin + url.pathname) - return hash([baseURL.href, JSON.stringify(config), imageHash]) - } - - // baseURL isn't a valid URL, but just a string used for an identifier - // use a relative path in the local case so that it's consistent across machines - const baseURL = new URL(url.protocol + path.relative(process.cwd(), url.pathname)) - return hash([baseURL.href, JSON.stringify(config), imageHash]) +export function generateImageID(config: ImageConfig, imageHash: string) { + return hash([JSON.stringify(config), imageHash]) } export function hash(keyParts: Array) {