diff --git a/.changeset/mean-tools-move.md b/.changeset/mean-tools-move.md new file mode 100644 index 000000000..5fb031e22 --- /dev/null +++ b/.changeset/mean-tools-move.md @@ -0,0 +1,5 @@ +--- +"@react-pdf/image": patch +--- + +refactor: image strict type checking diff --git a/packages/image/globals.d.ts b/packages/image/globals.d.ts index f34913956..1032d372a 100644 --- a/packages/image/globals.d.ts +++ b/packages/image/globals.d.ts @@ -1,5 +1,9 @@ import 'vitest-fetch-mock'; +declare module 'jay-peg'; + +declare module '@react-pdf/png-js'; + declare global { const BROWSER: boolean; } diff --git a/packages/image/src/cache.ts b/packages/image/src/cache.ts index c6f7d5776..0963914f5 100644 --- a/packages/image/src/cache.ts +++ b/packages/image/src/cache.ts @@ -1,13 +1,13 @@ const createCache = ({ limit = 100 } = {}) => { let cache: Record = {}; - let keys = []; + let keys: string[] = []; return { - get: (key: string): T | undefined => cache[key], + get: (key: string | null): T | null => (key ? cache[key] : null), set: (key: string, value: T) => { keys.push(key); if (keys.length > limit) { - delete cache[keys.shift()]; + delete cache[keys.shift()!]; } cache[key] = value; }, diff --git a/packages/image/src/jpeg.ts b/packages/image/src/jpeg.ts index 177ed14e1..f64d163ce 100644 --- a/packages/image/src/jpeg.ts +++ b/packages/image/src/jpeg.ts @@ -1,4 +1,5 @@ import _JPEG from 'jay-peg'; + import { Image } from './types'; class JPEG implements Image { @@ -10,6 +11,8 @@ class JPEG implements Image { constructor(data: Buffer) { this.data = data; this.format = 'jpeg'; + this.width = 0; + this.height = 0; if (data.readUInt16BE(0) !== 0xffd8) { throw new Error('SOI not found in JPEG'); @@ -37,7 +40,7 @@ class JPEG implements Image { } } - static isValid(data) { + static isValid(data: Buffer) { return data && Buffer.isBuffer(data) && data.readUInt16BE(0) === 0xffd8; } } diff --git a/packages/image/src/resolve.ts b/packages/image/src/resolve.ts index 7bd095e09..565ddee68 100644 --- a/packages/image/src/resolve.ts +++ b/packages/image/src/resolve.ts @@ -15,7 +15,7 @@ import { RemoteImageSrc, } from './types'; -export const IMAGE_CACHE = createCache>({ limit: 30 }); +export const IMAGE_CACHE = createCache>({ limit: 30 }); const isBuffer = Buffer.isBuffer; @@ -45,7 +45,7 @@ const getAbsoluteLocalPath = (src: string) => { path: pathname, } = url.parse(src); - const absolutePath = path.resolve(pathname); + const absolutePath = pathname ? path.resolve(pathname) : undefined; if ((protocol && protocol !== 'file:') || auth || host || port || hostname) { return undefined; @@ -123,12 +123,14 @@ function getImage(body: Buffer, format: string): Image | null { const resolveBase64Image = async ({ uri }: Base64ImageSrc) => { const match = /^data:image\/([a-zA-Z]*);base64,([^"]*)/g.exec(uri); + + if (!match) throw new Error(`Invalid base64 image: ${uri}`); + const format = match[1]; const data = match[2]; - if (!isValidFormat(format)) { + if (!isValidFormat(format)) throw new Error(`Base64 image invalid format: ${format}`); - } return getImage(Buffer.from(data, 'base64'), format); }; diff --git a/packages/image/tsconfig.json b/packages/image/tsconfig.json index 490d6d29a..816b77072 100644 --- a/packages/image/tsconfig.json +++ b/packages/image/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "Node", "esModuleInterop": true, "allowSyntheticDefaultImports": true, - "strict": false, + "strict": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "types": ["vitest/globals"],