diff --git a/.changeset/khaki-trees-try.md b/.changeset/khaki-trees-try.md new file mode 100644 index 0000000..7f128fd --- /dev/null +++ b/.changeset/khaki-trees-try.md @@ -0,0 +1,5 @@ +--- +"@fepack/react-image": patch +--- + +fix(react-image): change Load to return loaded HTMLImageElement diff --git a/.changeset/neat-trains-speak.md b/.changeset/neat-trains-speak.md new file mode 100644 index 0000000..42e88d1 --- /dev/null +++ b/.changeset/neat-trains-speak.md @@ -0,0 +1,5 @@ +--- +"@fepack/image": patch +--- + +fix(react-image): change Load to return loaded HTMLImageElement diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57336e2..1da0c32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,8 @@ jobs: cache: "pnpm" cache-dependency-path: "pnpm-lock.yaml" node-version-file: ".nvmrc" - - if: matrix.command == 'test' - run: pnpx playwright install - run: pnpm install --frozen-lockfile + - if: matrix.command == 'test' + run: pnpm exec playwright install - run: pnpm prepack - run: pnpm ${{ matrix.command }} diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index eed470c..38583b1 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -21,8 +21,8 @@ jobs: cache: "pnpm" cache-dependency-path: "pnpm-lock.yaml" node-version-file: ".nvmrc" - - run: pnpx playwright install - run: pnpm install --frozen-lockfile + - run: pnpm exec playwright install - run: pnpm test - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/package.json b/package.json index 3db7b08..e0fac3b 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "eslint": "^7.32.0", "jsdom": "^22.1.0", "packlint": "^0.2.4", + "playwright": "^1.40.1", "prettier": "^2.5.1", "publint": "^0.2.2", "rimraf": "^5.0.1", diff --git a/packages/image/package.json b/packages/image/package.json index 2a02371..160707d 100644 --- a/packages/image/package.json +++ b/packages/image/package.json @@ -51,8 +51,7 @@ "type:check": "tsc --noEmit" }, "devDependencies": { - "@types/node": "^18.16.2", - "playwright": "^1.38.0" + "@types/node": "^18.16.2" }, "publishConfig": { "access": "public" diff --git a/packages/image/src/LoadClient.ts b/packages/image/src/LoadClient.ts index 414d891..d0d875c 100644 --- a/packages/image/src/LoadClient.ts +++ b/packages/image/src/LoadClient.ts @@ -2,15 +2,15 @@ import { load } from "./load"; export type LoadSrc = Parameters[0]; -export type LoadState = { - src: TLoadSrc; +export type LoadState = { + image: HTMLImageElement; promise?: Promise; error?: unknown; }; type Notify = (...args: unknown[]) => unknown; export class LoadClient { - private loadCache = new Map>(); + private loadCache = new Map(); private notifiesMap = new Map(); attach(src: LoadSrc, notify: Notify) { @@ -38,17 +38,17 @@ export class LoadClient { if (loadState?.error) { throw loadState.error; } - if (loadState?.src) { - return loadState as LoadState; + if (loadState?.image) { + return loadState.image; } if (loadState?.promise) { throw loadState.promise; } - const newLoadState: LoadState = { - src, + const newLoadState: LoadState = { + image: undefined as unknown as HTMLImageElement, promise: load(src) - .then((image) => (newLoadState.src = image.src as TLoadSrc)) + .then((image) => (newLoadState.image = image)) .catch(() => (newLoadState.error = `${src}: load error`)), }; diff --git a/packages/image/src/load.spec.ts b/packages/image/src/load.spec.ts index 1805054..522f62e 100644 --- a/packages/image/src/load.spec.ts +++ b/packages/image/src/load.spec.ts @@ -3,7 +3,7 @@ import { load } from "."; describe("load", () => { it("should load image by src", async () => { - const loadedImage = await load("src/images/test.png"); - expect(loadedImage.src).toBe("http://localhost:5173/src/images/test.png"); + const image = await load("src/images/test.png"); + expect(image.src).toBe("http://localhost:5173/src/images/test.png"); }); }); diff --git a/packages/react-image/package.json b/packages/react-image/package.json index b5299fd..e086b44 100644 --- a/packages/react-image/package.json +++ b/packages/react-image/package.json @@ -47,8 +47,7 @@ "type:check": "tsc --noEmit" }, "dependencies": { - "@fepack/image": "workspace:0.2.5", - "use-sync-external-store": "^1.2.0" + "@fepack/image": "workspace:*" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", @@ -57,12 +56,11 @@ "@types/react": "^18.2.0", "@types/react-dom": "^18.2.1", "@types/testing-library__jest-dom": "^5.14.5", - "@types/use-sync-external-store": "^0.0.4", "react": "^18.2.0", "react-dom": "^18.2.0" }, "peerDependencies": { - "react": "^16.8 || ^17 || ^18" + "react": "^18" }, "publishConfig": { "access": "public" diff --git a/packages/react-image/src/Load.ts b/packages/react-image/src/Load.ts index cbf728d..9305a29 100644 --- a/packages/react-image/src/Load.ts +++ b/packages/react-image/src/Load.ts @@ -1,6 +1,5 @@ -import { LoadClient, type LoadSrc, type LoadState } from "@fepack/image"; -import { type FunctionComponent, createElement } from "react"; -import { useSyncExternalStore } from "use-sync-external-store/shim"; +import { LoadClient, type LoadSrc } from "@fepack/image"; +import { ReactNode, useSyncExternalStore } from "react"; const loadClient = new LoadClient(); @@ -18,9 +17,9 @@ export const useLoad = ( type LoadProps = { src: TLoadSrc; - children: FunctionComponent>; + children: (loadedImage: HTMLImageElement) => ReactNode; }; export const Load = ({ src, children, -}: LoadProps) => createElement(children, useLoad({ src })); +}: LoadProps) => children(useLoad({ src })); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b07442..0b047ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: packlint: specifier: ^0.2.4 version: 0.2.4(typanion@3.14.0) + playwright: + specifier: ^1.40.1 + version: 1.40.1 prettier: specifier: ^2.5.1 version: 2.8.8 @@ -55,13 +58,13 @@ importers: version: 7.2.0(typescript@5.1.6) turbo: specifier: latest - version: 1.10.14 + version: 1.10.16 typescript: specifier: 5.1.6 version: 5.1.6 vitest: specifier: ^0.34.3 - version: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0) + version: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1) configs/eslint-config: dependencies: @@ -129,18 +132,12 @@ importers: '@types/node': specifier: ^18.16.2 version: 18.17.16 - playwright: - specifier: ^1.38.0 - version: 1.38.0 packages/react-image: dependencies: '@fepack/image': - specifier: workspace:0.2.5 + specifier: workspace:* version: link:../image - use-sync-external-store: - specifier: ^1.2.0 - version: 1.2.0(react@18.2.0) devDependencies: '@testing-library/jest-dom': specifier: ^5.16.5 @@ -160,9 +157,6 @@ importers: '@types/testing-library__jest-dom': specifier: ^5.14.5 version: 5.14.9 - '@types/use-sync-external-store': - specifier: ^0.0.4 - version: 0.0.4 react: specifier: ^18.2.0 version: 18.2.0 @@ -4052,10 +4046,6 @@ packages: resolution: {integrity: sha512-d0XxK3YTObnWVp6rZuev3c49+j4Lo8g4L1ZRm9z5L0xpoZycUPshHgczK5gsUMaZOstjVYYi09p5gYvUtfChYw==} dev: false - /@types/use-sync-external-store@0.0.4: - resolution: {integrity: sha512-DMBc2WDEfaGsWXqH/Sk2oBaUkvlUwqgt/YEygpqX0MaiEjqR7afd1QgE4Pq2zBr/TRz0Mpu92eBBo5UQjtTD5Q==} - dev: true - /@types/ws@8.5.5: resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==} dependencies: @@ -4270,7 +4260,7 @@ packages: magic-string: 0.30.3 modern-node-polyfills: 0.1.3(rollup@2.79.1) sirv: 2.0.3 - vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0) + vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1) transitivePeerDependencies: - rollup dev: true @@ -4287,7 +4277,7 @@ packages: istanbul-reports: 3.1.6 picocolors: 1.0.0 test-exclude: 6.0.0 - vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0) + vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1) transitivePeerDependencies: - supports-color dev: true @@ -4334,7 +4324,7 @@ packages: pathe: 1.1.1 picocolors: 1.0.0 sirv: 2.0.3 - vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0) + vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1) dev: true /@vitest/utils@0.34.4: @@ -6967,7 +6957,7 @@ packages: '@typescript-eslint/utils': 6.7.0(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 typescript: 5.2.2 - vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0) + vitest: 0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1) transitivePeerDependencies: - supports-color dev: true @@ -9985,18 +9975,18 @@ packages: find-up: 3.0.0 dev: false - /playwright-core@1.38.0: - resolution: {integrity: sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==} + /playwright-core@1.40.1: + resolution: {integrity: sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==} engines: {node: '>=16'} hasBin: true dev: true - /playwright@1.38.0: - resolution: {integrity: sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==} + /playwright@1.40.1: + resolution: {integrity: sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==} engines: {node: '>=16'} hasBin: true dependencies: - playwright-core: 1.38.0 + playwright-core: 1.40.1 optionalDependencies: fsevents: 2.3.2 dev: true @@ -12221,64 +12211,64 @@ packages: yargs: 17.7.2 dev: true - /turbo-darwin-64@1.10.14: - resolution: {integrity: sha512-I8RtFk1b9UILAExPdG/XRgGQz95nmXPE7OiGb6ytjtNIR5/UZBS/xVX/7HYpCdmfriKdVwBKhalCoV4oDvAGEg==} + /turbo-darwin-64@1.10.16: + resolution: {integrity: sha512-+Jk91FNcp9e9NCLYlvDDlp2HwEDp14F9N42IoW3dmHI5ZkGSXzalbhVcrx3DOox3QfiNUHxzWg4d7CnVNCuuMg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.10.14: - resolution: {integrity: sha512-KAdUWryJi/XX7OD0alOuOa0aJ5TLyd4DNIYkHPHYcM6/d7YAovYvxRNwmx9iv6Vx6IkzTnLeTiUB8zy69QkG9Q==} + /turbo-darwin-arm64@1.10.16: + resolution: {integrity: sha512-jqGpFZipIivkRp/i+jnL8npX0VssE6IAVNKtu573LXtssZdV/S+fRGYA16tI46xJGxSAivrZ/IcgZrV6Jk80bw==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.10.14: - resolution: {integrity: sha512-BOBzoREC2u4Vgpap/WDxM6wETVqVMRcM8OZw4hWzqCj2bqbQ6L0wxs1LCLWVrghQf93JBQtIGAdFFLyCSBXjWQ==} + /turbo-linux-64@1.10.16: + resolution: {integrity: sha512-PpqEZHwLoizQ6sTUvmImcRmACyRk9EWLXGlqceogPZsJ1jTRK3sfcF9fC2W56zkSIzuLEP07k5kl+ZxJd8JMcg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.10.14: - resolution: {integrity: sha512-D8T6XxoTdN5D4V5qE2VZG+/lbZX/89BkAEHzXcsSUTRjrwfMepT3d2z8aT6hxv4yu8EDdooZq/2Bn/vjMI32xw==} + /turbo-linux-arm64@1.10.16: + resolution: {integrity: sha512-TMjFYz8to1QE0fKVXCIvG/4giyfnmqcQIwjdNfJvKjBxn22PpbjeuFuQ5kNXshUTRaTJihFbuuCcb5OYFNx4uw==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.10.14: - resolution: {integrity: sha512-zKNS3c1w4i6432N0cexZ20r/aIhV62g69opUn82FLVs/zk3Ie0GVkSB6h0rqIvMalCp7enIR87LkPSDGz9K4UA==} + /turbo-windows-64@1.10.16: + resolution: {integrity: sha512-+jsf68krs0N66FfC4/zZvioUap/Tq3sPFumnMV+EBo8jFdqs4yehd6+MxIwYTjSQLIcpH8KoNMB0gQYhJRLZzw==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.10.14: - resolution: {integrity: sha512-rkBwrTPTxNSOUF7of8eVvvM+BkfkhA2OvpHM94if8tVsU+khrjglilp8MTVPHlyS9byfemPAmFN90oRIPB05BA==} + /turbo-windows-arm64@1.10.16: + resolution: {integrity: sha512-sKm3hcMM1bl0B3PLG4ifidicOGfoJmOEacM5JtgBkYM48ncMHjkHfFY7HrJHZHUnXM4l05RQTpLFoOl/uIo2HQ==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.10.14: - resolution: {integrity: sha512-hr9wDNYcsee+vLkCDIm8qTtwhJ6+UAMJc3nIY6+PNgUTtXcQgHxCq8BGoL7gbABvNWv76CNbK5qL4Lp9G3ZYRA==} + /turbo@1.10.16: + resolution: {integrity: sha512-2CEaK4FIuSZiP83iFa9GqMTQhroW2QryckVqUydmg4tx78baftTOS0O+oDAhvo9r9Nit4xUEtC1RAHoqs6ZEtg==} hasBin: true optionalDependencies: - turbo-darwin-64: 1.10.14 - turbo-darwin-arm64: 1.10.14 - turbo-linux-64: 1.10.14 - turbo-linux-arm64: 1.10.14 - turbo-windows-64: 1.10.14 - turbo-windows-arm64: 1.10.14 + turbo-darwin-64: 1.10.16 + turbo-darwin-arm64: 1.10.16 + turbo-linux-64: 1.10.16 + turbo-linux-arm64: 1.10.16 + turbo-windows-64: 1.10.16 + turbo-windows-arm64: 1.10.16 dev: true /typanion@3.14.0: @@ -12637,14 +12627,6 @@ packages: react: 17.0.2 dev: false - /use-sync-external-store@1.2.0(react@18.2.0): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false - /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: false @@ -12764,7 +12746,7 @@ packages: fsevents: 2.3.3 dev: true - /vitest@0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0): + /vitest@0.34.4(@vitest/browser@0.34.4)(@vitest/ui@0.34.4)(jsdom@22.1.0)(playwright@1.40.1): resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==} engines: {node: '>=v14.18.0'} hasBin: true @@ -12815,6 +12797,7 @@ packages: magic-string: 0.30.3 pathe: 1.1.1 picocolors: 1.0.0 + playwright: 1.40.1 std-env: 3.4.3 strip-literal: 1.3.0 tinybench: 2.5.1 diff --git a/websites/demo-react-image-next/public/images/test.png b/websites/demo-react-image-next/public/images/test.png new file mode 100644 index 0000000..73528e2 Binary files /dev/null and b/websites/demo-react-image-next/public/images/test.png differ diff --git a/websites/demo-react-image-next/src/app/load/page.tsx b/websites/demo-react-image-next/src/app/load/page.tsx index 68d36a6..a15fb4e 100644 --- a/websites/demo-react-image-next/src/app/load/page.tsx +++ b/websites/demo-react-image-next/src/app/load/page.tsx @@ -12,7 +12,7 @@ export default function Page() { }> - {(loaded) => } + {(image) => } diff --git a/websites/demo-react-image-next/src/app/page.tsx b/websites/demo-react-image-next/src/app/page.tsx index b415280..218075d 100644 --- a/websites/demo-react-image-next/src/app/page.tsx +++ b/websites/demo-react-image-next/src/app/page.tsx @@ -1,28 +1,64 @@ "use client"; -import { Load } from "@fepack/react-image"; +import { Load, extractRGBAs } from "@fepack/react-image"; import { ErrorBoundary, Suspense } from "@suspensive/react"; import Link from "next/link"; +import { useState } from "react"; import { Spinner } from "~/components/uis"; export default function Home() { + const [size, setSize] = useState(8); + return (
to load <>load image error}> }> - {(loaded) => } + {(image) => } <>load image error}> }> - {(loaded) => } + {(image) => } + setSize(Number(e.target.value))} + /> + + + + {(image) => ( +
+ {extractRGBAs(image).map((rgba, index) => ( +
+ ))} +
+ )} + +
); }