From 3be4d1d39d9801b4847f350dfe16aeeae1dfc5d3 Mon Sep 17 00:00:00 2001 From: Ze-Zheng Wu Date: Thu, 4 Jan 2024 01:58:37 +0800 Subject: [PATCH] feat: add react hooks --- package-lock.json | 55 +++++++++++++++++++++++++++++++ package.json | 26 +++++++++++++-- scripts/build-iife.ts | 9 ++++- src/react/full/index.ts | 1 + src/react/full/useZXingWasm.ts | 56 ++++++++++++++++++++++++++++++++ src/react/reader/index.ts | 1 + src/react/reader/useZXingWasm.ts | 45 +++++++++++++++++++++++++ src/react/writer/index.ts | 1 + src/react/writer/useZXingWasm.ts | 36 ++++++++++++++++++++ typedoc.json | 6 +++- vite.config.ts | 4 +++ 11 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 src/react/full/index.ts create mode 100644 src/react/full/useZXingWasm.ts create mode 100644 src/react/reader/index.ts create mode 100644 src/react/reader/useZXingWasm.ts create mode 100644 src/react/writer/index.ts create mode 100644 src/react/writer/useZXingWasm.ts diff --git a/package-lock.json b/package-lock.json index 13ad2d53..8fe64831 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@babel/types": "^7.23.6", "@types/babel__core": "^7.20.5", "@types/node": "^20.10.6", + "@types/react": "^18.2.46", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "concurrently": "^8.2.2", @@ -23,6 +24,7 @@ "eslint": "^8.56.0", "npm-check-updates": "^16.14.12", "prettier": "^3.1.1", + "react": "^18.2.0", "rimraf": "^5.0.5", "tsx": "^4.7.0", "typedoc": "^0.25.6", @@ -1761,6 +1763,29 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.46", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.46.tgz", + "integrity": "sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", @@ -2781,6 +2806,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -4276,6 +4307,18 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lowercase-keys": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", @@ -5689,6 +5732,18 @@ "node": ">=0.10.0" } }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-package-json": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", diff --git a/package.json b/package.json index 2a498f9e..f7450538 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,27 @@ }, "./reader/zxing_reader.wasm": "./dist/reader/zxing_reader.wasm", "./writer/zxing_writer.wasm": "./dist/writer/zxing_writer.wasm", - "./full/zxing_full.wasm": "./dist/full/zxing_full.wasm" + "./full/zxing_full.wasm": "./dist/full/zxing_full.wasm", + "./react": { + "import": "./dist/es/react/full/index.js", + "require": "./dist/cjs/react/full/index.js", + "default": "./dist/es/react/full/index.js" + }, + "./react/full": { + "import": "./dist/es/react/full/index.js", + "require": "./dist/cjs/react/full/index.js", + "default": "./dist/es/react/full/index.js" + }, + "./react/reader": { + "import": "./dist/es/react/reader/index.js", + "require": "./dist/cjs/react/reader/index.js", + "default": "./dist/es/react/reader/index.js" + }, + "./react/writer": { + "import": "./dist/es/react/writer/index.js", + "require": "./dist/cjs/react/writer/index.js", + "default": "./dist/es/react/writer/index.js" + } }, "repository": { "type": "git", @@ -41,7 +61,7 @@ "homepage": "https://github.com/Sec-ant/zxing-wasm", "bugs": { "url": "https://github.com/Sec-ant/zxing-wasm/issues", - "email": "zzwu@zju.edu.cn" + "email": "zezhengwu@proton.me" }, "keywords": [ "qrcode", @@ -106,6 +126,7 @@ "@babel/types": "^7.23.6", "@types/babel__core": "^7.20.5", "@types/node": "^20.10.6", + "@types/react": "^18.2.46", "@typescript-eslint/eslint-plugin": "^6.17.0", "@typescript-eslint/parser": "^6.17.0", "concurrently": "^8.2.2", @@ -113,6 +134,7 @@ "eslint": "^8.56.0", "npm-check-updates": "^16.14.12", "prettier": "^3.1.1", + "react": "^18.2.0", "rimraf": "^5.0.5", "tsx": "^4.7.0", "typedoc": "^0.25.6", diff --git a/scripts/build-iife.ts b/scripts/build-iife.ts index 2bf2426d..6b76f198 100644 --- a/scripts/build-iife.ts +++ b/scripts/build-iife.ts @@ -19,7 +19,14 @@ async function buildIife() { formats: ["iife"], name: "ZXingWASM", }, - rollupOptions: undefined, + rollupOptions: { + external: ["react"], + output: { + globals: { + react: "React", + }, + }, + }, outDir: "dist/iife", emptyOutDir: false, }, diff --git a/src/react/full/index.ts b/src/react/full/index.ts new file mode 100644 index 00000000..9c268a8a --- /dev/null +++ b/src/react/full/index.ts @@ -0,0 +1 @@ +export * from "./useZXingWasm.js"; diff --git a/src/react/full/useZXingWasm.ts b/src/react/full/useZXingWasm.ts new file mode 100644 index 00000000..6d1b7af3 --- /dev/null +++ b/src/react/full/useZXingWasm.ts @@ -0,0 +1,56 @@ +import { useCallback, useEffect } from "react"; +import { + type ReaderOptions, + readBarcodesFromImageData as _readBarcodesFromImageData, + readBarcodesFromImageFile as _readBarcodesFromImageFile, + type WriterOptions, + writeBarcodeToImageFile as _writeBarcodeToImageFile, + setZXingModuleOverrides, +} from "../../full/index.js"; + +export const useZXingWasm = ( + wasmLocation?: string, + readerOptions?: ReaderOptions, + writerOptions?: WriterOptions, +) => { + // re-init zxing module when wasm location changes + useEffect(() => { + if (typeof wasmLocation === "undefined") { + return; + } + setZXingModuleOverrides({ + locateFile: (path, prefix) => { + if (path.endsWith(".wasm")) { + return wasmLocation; + } + return prefix + path; + }, + }); + }, [wasmLocation]); + // readBarcodesFromImageData + const readBarcodesFromImageData = useCallback( + (image: ImageData) => { + return _readBarcodesFromImageData(image, readerOptions); + }, + [readerOptions], + ); + // readBarcodesFromImageFile + const readBarcodesFromImageFile = useCallback( + (image: Blob) => { + return _readBarcodesFromImageFile(image, readerOptions); + }, + [readerOptions], + ); + // writeBarcodeToImageFile + const writeBarcodeToImageFile = useCallback( + (text: string) => { + return _writeBarcodeToImageFile(text, writerOptions); + }, + [writerOptions], + ); + return { + readBarcodesFromImageData, + readBarcodesFromImageFile, + writeBarcodeToImageFile, + }; +}; diff --git a/src/react/reader/index.ts b/src/react/reader/index.ts new file mode 100644 index 00000000..9c268a8a --- /dev/null +++ b/src/react/reader/index.ts @@ -0,0 +1 @@ +export * from "./useZXingWasm.js"; diff --git a/src/react/reader/useZXingWasm.ts b/src/react/reader/useZXingWasm.ts new file mode 100644 index 00000000..24a874c9 --- /dev/null +++ b/src/react/reader/useZXingWasm.ts @@ -0,0 +1,45 @@ +import { useCallback, useEffect } from "react"; +import { + type ReaderOptions, + readBarcodesFromImageData as _readBarcodesFromImageData, + readBarcodesFromImageFile as _readBarcodesFromImageFile, + setZXingModuleOverrides, +} from "../../reader/index.js"; + +export const useZXingWasm = ( + wasmLocation?: string, + readerOptions?: ReaderOptions, +) => { + // re-init zxing module when wasm location changes + useEffect(() => { + if (typeof wasmLocation === "undefined") { + return; + } + setZXingModuleOverrides({ + locateFile: (path, prefix) => { + if (path.endsWith(".wasm")) { + return wasmLocation; + } + return prefix + path; + }, + }); + }, [wasmLocation]); + // readBarcodesFromImageData + const readBarcodesFromImageData = useCallback( + (image: ImageData) => { + return _readBarcodesFromImageData(image, readerOptions); + }, + [readerOptions], + ); + // readBarcodesFromImageFile + const readBarcodesFromImageFile = useCallback( + (image: Blob) => { + return _readBarcodesFromImageFile(image, readerOptions); + }, + [readerOptions], + ); + return { + readBarcodesFromImageData, + readBarcodesFromImageFile, + }; +}; diff --git a/src/react/writer/index.ts b/src/react/writer/index.ts new file mode 100644 index 00000000..9c268a8a --- /dev/null +++ b/src/react/writer/index.ts @@ -0,0 +1 @@ +export * from "./useZXingWasm.js"; diff --git a/src/react/writer/useZXingWasm.ts b/src/react/writer/useZXingWasm.ts new file mode 100644 index 00000000..0647bbbb --- /dev/null +++ b/src/react/writer/useZXingWasm.ts @@ -0,0 +1,36 @@ +import { useCallback, useEffect } from "react"; +import { + type WriterOptions, + writeBarcodeToImageFile as _writeBarcodeToImageFile, + setZXingModuleOverrides, +} from "../../writer/index.js"; + +export const useZXingWasm = ( + wasmLocation?: string, + writerOptions?: WriterOptions, +) => { + // re-init zxing module when wasm location changes + useEffect(() => { + if (typeof wasmLocation === "undefined") { + return; + } + setZXingModuleOverrides({ + locateFile: (path, prefix) => { + if (path.endsWith(".wasm")) { + return wasmLocation; + } + return prefix + path; + }, + }); + }, [wasmLocation]); + // writeBarcodeToImageFile + const writeBarcodeToImageFile = useCallback( + (text: string) => { + return _writeBarcodeToImageFile(text, writerOptions); + }, + [writerOptions], + ); + return { + writeBarcodeToImageFile, + }; +}; diff --git a/typedoc.json b/typedoc.json index dddddcdd..a80df3b0 100644 --- a/typedoc.json +++ b/typedoc.json @@ -3,7 +3,11 @@ "entryPoints": [ "./src/full/index.ts", "./src/reader/index.ts", - "./src/writer/index.ts" + "./src/writer/index.ts", + "./src/react/full/index.ts", + "./src/react/reader/index.ts", + "./src/react/writer/index.ts" ], + "sortEntryPoints": false, "out": "docs" } diff --git a/vite.config.ts b/vite.config.ts index 67319fdd..ebc6c2a9 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -67,12 +67,16 @@ export default defineConfig({ "reader/index": "src/reader/index.ts", "writer/index": "src/writer/index.ts", "full/index": "src/full/index.ts", + "react/reader/index": "src/react/reader/index.ts", + "react/writer/index": "src/react/writer/index.ts", + "react/full/index": "src/react/full/index.ts", }, formats: ["es"], fileName: (_, entryName) => `${entryName}.js`, }, outDir: "dist/es", rollupOptions: { + external: ["react"], output: { manualChunks: (id) => { if (