diff --git a/biome.json b/biome.json index 5c541605..af464978 100644 --- a/biome.json +++ b/biome.json @@ -1,42 +1,39 @@ { - "$schema": "https://biomejs.dev/schemas/2.1.1/schema.json", - "vcs": { - "enabled": false, - "clientKind": "git", - "useIgnoreFile": false - }, - "files": { - "ignoreUnknown": false, - "includes": [ - "**", - "!**/lib/**", - "!**/ios/**", - "!**/android/**", - "!**/nitrogen/generated/**" - ] + "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": true, + "includes": ["**", "!node_modules"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true }, + "domains": { + "react": "recommended" + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + }, + "javascript": { "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 4 - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "on" - } - } + "quoteStyle": "single", + "semicolons": "asNeeded" } + } } diff --git a/bun.lock b/bun.lock index b1c14200..54cfbfc6 100644 --- a/bun.lock +++ b/bun.lock @@ -5,13 +5,13 @@ "": { "name": "react-native-nitro-image-monorepo", "devDependencies": { + "@biomejs/biome": "^2.3.5", "@release-it-plugins/workspaces": "^5.0.3", "@release-it/bumper": "^7.0.5", "@release-it/conventional-changelog": "^10.0.1", "@tsconfig/react-native": "^2.0.3", "@types/jest": "^30.0.0", "@types/react": "^19.1.8", - "prettier": "^3.6.2", "react": "19.1.0", "react-native": "0.81.0", "react-native-builder-bob": "^0.37.0", @@ -52,7 +52,6 @@ "name": "react-native-nitro-image", "version": "0.10.2", "devDependencies": { - "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", @@ -70,7 +69,6 @@ "name": "react-native-nitro-web-image", "version": "0.10.2", "devDependencies": { - "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", @@ -335,23 +333,23 @@ "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@biomejs/biome": ["@biomejs/biome@2.2.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.2.6", "@biomejs/cli-darwin-x64": "2.2.6", "@biomejs/cli-linux-arm64": "2.2.6", "@biomejs/cli-linux-arm64-musl": "2.2.6", "@biomejs/cli-linux-x64": "2.2.6", "@biomejs/cli-linux-x64-musl": "2.2.6", "@biomejs/cli-win32-arm64": "2.2.6", "@biomejs/cli-win32-x64": "2.2.6" }, "bin": { "biome": "bin/biome" } }, "sha512-yKTCNGhek0rL5OEW1jbLeZX8LHaM8yk7+3JRGv08my+gkpmtb5dDE+54r2ZjZx0ediFEn1pYBOJSmOdDP9xtFw=="], + "@biomejs/biome": ["@biomejs/biome@2.3.14", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.14", "@biomejs/cli-darwin-x64": "2.3.14", "@biomejs/cli-linux-arm64": "2.3.14", "@biomejs/cli-linux-arm64-musl": "2.3.14", "@biomejs/cli-linux-x64": "2.3.14", "@biomejs/cli-linux-x64-musl": "2.3.14", "@biomejs/cli-win32-arm64": "2.3.14", "@biomejs/cli-win32-x64": "2.3.14" }, "bin": { "biome": "bin/biome" } }, "sha512-QMT6QviX0WqXJCaiqVMiBUCr5WRQ1iFSjvOLoTk6auKukJMvnMzWucXpwZB0e8F00/1/BsS9DzcKgWH+CLqVuA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.2.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UZPmn3M45CjTYulgcrFJFZv7YmK3pTxTJDrFYlNElT2FNnkkX4fsxjExTSMeWKQYoZjvekpH5cvrYZZlWu3yfA=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UJGPpvWJMkLxSRtpCAKfKh41Q4JJXisvxZL8ChN1eNW3m/WlPFJ6EFDCE7YfUb4XS8ZFi3C1dFpxUJ0Ety5n+A=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.2.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-HOUIquhHVgh/jvxyClpwlpl/oeMqntlteL89YqjuFDiZ091P0vhHccwz+8muu3nTyHWM5FQslt+4Jdcd67+xWQ=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-PNkLNQG6RLo8lG7QoWe/hhnMxJIt1tEimoXpGQjwS/dkdNiKBLPv4RpeQl8o3s1OKI3ZOR5XPiYtmbGGHAOnLA=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-BpGtuMJGN+o8pQjvYsUKZ+4JEErxdSmcRD/JG3mXoWc6zrcA7OkuyGFN1mDggO0Q1n7qXxo/PcupHk8gzijt5g=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-KT67FKfzIw6DNnUNdYlBg+eU24Go3n75GWK6NwU4+yJmDYFe9i/MjiI+U/iEzKvo0g7G7MZqoyrhIYuND2w8QQ=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.2.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-TjCenQq3N6g1C+5UT3jE1bIiJb5MWQvulpUngTIpFsL4StVAUXucWD0SL9MCW89Tm6awWfeXBbZBAhJwjyFbRQ=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-LInRbXhYujtL3sH2TMCH/UBwJZsoGwfQjBrMfl84CD4hL/41C/EU5mldqf1yoFpsI0iPWuU83U+nB2TUUypWeg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1HaM/dpI/1Z68zp8ZdT6EiBq+/O/z97a2AiHMl+VAdv5/ELckFt9EvRb8hDHpk8hUMoz03gXkC7VPXOVtU7faA=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-ZsZzQsl9U+wxFrGGS4f6UxREUlgHwmEfu1IrXlgNFrNnd5Th6lIJr8KmSzu/+meSa9f4rzFrbEW9LBBA6ScoMA=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.2.6", "", { "os": "linux", "cpu": "x64" }, "sha512-1ZcBux8zVM3JhWN2ZCPaYf0+ogxXG316uaoXJdgoPZcdK/rmRcRY7PqHdAos2ExzvjIdvhQp72UcveI98hgOog=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-KQU7EkbBBuHPW3/rAcoiVmhlPtDSGOGRPv9js7qJVpYTzjQmVR+C9Rfcz+ti8YCH+zT1J52tuBybtP4IodjxZQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.2.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-h3A88G8PGM1ryTeZyLlSdfC/gz3e95EJw9BZmA6Po412DRqwqPBa2Y9U+4ZSGUAXCsnSQE00jLV8Pyrh0d+jQw=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-+IKYkj/pUBbnRf1G1+RlyA3LWiDgra1xpS7H2g4BuOzzRbRB+hmlw0yFsLprHhbbt7jUzbzAbAjK/Pn0FDnh1A=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.6", "", { "os": "win32", "cpu": "x64" }, "sha512-yx0CqeOhPjYQ5ZXgPfu8QYkgBhVJyvWe36as7jRuPrKPO5ylVDfwVtPQ+K/mooNTADW0IhxOZm3aPu16dP8yNQ=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-oizCjdyQ3WJEswpb3Chdngeat56rIdSYK12JI3iI11Mt5T5EXcZ7WLuowzEaFPNJ3zmOQFliMN8QY1Pi+qsfdQ=="], "@conventional-changelog/git-client": ["@conventional-changelog/git-client@2.5.1", "", { "dependencies": { "@simple-libs/child-process-utils": "^1.0.0", "@simple-libs/stream-utils": "^1.1.0", "semver": "^7.5.2" }, "peerDependencies": { "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.1.0" }, "optionalPeers": ["conventional-commits-filter", "conventional-commits-parser"] }, "sha512-lAw7iA5oTPWOLjiweb7DlGEMDEvzqzLLa6aWOly2FSZ64IwLE8T458rC+o+WvI31Doz6joM7X2DoNog7mX8r4A=="], @@ -1365,8 +1363,6 @@ "pkg-up": ["pkg-up@3.1.0", "", { "dependencies": { "find-up": "^3.0.0" } }, "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="], - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], - "pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], @@ -1775,9 +1771,9 @@ "@ts-morph/common/minimatch": ["minimatch@10.1.2", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.1" } }, "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw=="], - "NitroImageExample/react-native-nitro-image": ["react-native-nitro-image@file:packages/react-native-nitro-image", { "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }], + "NitroImageExample/react-native-nitro-image": ["react-native-nitro-image@file:packages/react-native-nitro-image", { "devDependencies": { "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }], - "NitroImageExample/react-native-nitro-web-image": ["react-native-nitro-web-image@file:packages/react-native-nitro-web-image", { "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-image": "*", "react-native-nitro-modules": "*" } }], + "NitroImageExample/react-native-nitro-web-image": ["react-native-nitro-web-image@file:packages/react-native-nitro-web-image", { "devDependencies": { "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-image": "*", "react-native-nitro-modules": "*" } }], "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -2119,7 +2115,7 @@ "@simple-libs/stream-utils/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "NitroImageExample/react-native-nitro-web-image/react-native-nitro-image": ["react-native-nitro-image@file:packages/react-native-nitro-image", { "devDependencies": { "@biomejs/biome": "2.2.6", "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }], + "NitroImageExample/react-native-nitro-web-image/react-native-nitro-image": ["react-native-nitro-image@file:packages/react-native-nitro-image", { "devDependencies": { "@types/react": "^19.0.6", "nitrogen": "0.33.4", "react": "19.1.0", "react-native": "0.81.0", "react-native-nitro-modules": "0.33.4", "typescript": "5.8.3" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-nitro-modules": "*" } }], "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], diff --git a/example/app.json b/example/app.json index 214d4030..b571d357 100644 --- a/example/app.json +++ b/example/app.json @@ -1,4 +1,4 @@ { - "name": "NitroImageExample", - "displayName": "NitroImageExample" + "name": "NitroImageExample", + "displayName": "NitroImageExample" } diff --git a/example/babel.config.js b/example/babel.config.js index f8228843..3e0218e6 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: ["module:@react-native/babel-preset"], -}; + presets: ['module:@react-native/babel-preset'], +} diff --git a/example/index.js b/example/index.js index 11cbe241..c6f88c40 100644 --- a/example/index.js +++ b/example/index.js @@ -2,8 +2,8 @@ * @format */ -import { AppRegistry } from "react-native"; -import { name as appName } from "./app.json"; -import App from "./src/App"; +import { AppRegistry } from 'react-native' +import { name as appName } from './app.json' +import App from './src/App' -AppRegistry.registerComponent(appName, () => App); +AppRegistry.registerComponent(appName, () => App) diff --git a/example/ios/NitroImageExample/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/NitroImageExample/Images.xcassets/AppIcon.appiconset/Contents.json index bb8673b9..ddd7fca8 100644 --- a/example/ios/NitroImageExample/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/example/ios/NitroImageExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,53 +1,53 @@ { - "images": [ - { - "idiom": "iphone", - "scale": "2x", - "size": "20x20" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "20x20" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "29x29" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "29x29" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "40x40" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "40x40" - }, - { - "idiom": "iphone", - "scale": "2x", - "size": "60x60" - }, - { - "idiom": "iphone", - "scale": "3x", - "size": "60x60" - }, - { - "idiom": "ios-marketing", - "scale": "1x", - "size": "1024x1024" - } - ], - "info": { - "author": "xcode", - "version": 1 - } + "images": [ + { + "idiom": "iphone", + "scale": "2x", + "size": "20x20" + }, + { + "idiom": "iphone", + "scale": "3x", + "size": "20x20" + }, + { + "idiom": "iphone", + "scale": "2x", + "size": "29x29" + }, + { + "idiom": "iphone", + "scale": "3x", + "size": "29x29" + }, + { + "idiom": "iphone", + "scale": "2x", + "size": "40x40" + }, + { + "idiom": "iphone", + "scale": "3x", + "size": "40x40" + }, + { + "idiom": "iphone", + "scale": "2x", + "size": "60x60" + }, + { + "idiom": "iphone", + "scale": "3x", + "size": "60x60" + }, + { + "idiom": "ios-marketing", + "scale": "1x", + "size": "1024x1024" + } + ], + "info": { + "author": "xcode", + "version": 1 + } } diff --git a/example/ios/NitroImageExample/Images.xcassets/Contents.json b/example/ios/NitroImageExample/Images.xcassets/Contents.json index 9a38aea4..97a8662e 100644 --- a/example/ios/NitroImageExample/Images.xcassets/Contents.json +++ b/example/ios/NitroImageExample/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { - "info": { - "version": 1, - "author": "xcode" - } + "info": { + "version": 1, + "author": "xcode" + } } diff --git a/example/metro.config.js b/example/metro.config.js index 5df2f5ee..4f33efbe 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -1,7 +1,7 @@ -const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); -const path = require("node:path"); +const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config') +const path = require('node:path') -const root = path.resolve(__dirname, ".."); +const root = path.resolve(__dirname, '..') /** * Metro configuration @@ -10,17 +10,17 @@ const root = path.resolve(__dirname, ".."); * @type {import('@react-native/metro-config').MetroConfig} */ const config = { - watchFolders: [root], + watchFolders: [root], - transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: true, - }, - }), - }, -}; + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + }, +} -module.exports = mergeConfig(getDefaultConfig(__dirname), config); -console.log(module.exports); +module.exports = mergeConfig(getDefaultConfig(__dirname), config) +console.log(module.exports) diff --git a/example/package.json b/example/package.json index 2ff417cb..9d15a188 100644 --- a/example/package.json +++ b/example/package.json @@ -1,42 +1,41 @@ { - "name": "NitroImageExample", - "version": "0.10.2", - "private": true, - "scripts": { - "android": "react-native run-android", - "ios": "react-native run-ios", - "lint": "biome check .", - "start": "react-native start --client-logs", - "pods": "bundle install && cd ios && bundle exec pod install", - "bundle-install": "bundle install", - "build:android-release": "cd android && ./gradlew assembleRelease --no-daemon" - }, - "dependencies": { - "@react-navigation/bottom-tabs": "^7.4.6", - "@react-navigation/native": "^7.1.17", - "react": "19.1.0", - "react-native": "0.81.0", - "react-native-fast-image": "^8.6.3", - "react-native-nitro-image": "../packages/react-native-nitro-image", - "react-native-nitro-web-image": "../packages/react-native-nitro-web-image", - "react-native-nitro-modules": "0.33.4", - "react-native-safe-area-context": "^5.6.0", - "react-native-screens": "^4.14.1" - }, - "devDependencies": { - "@babel/core": "^7.27.4", - "@babel/preset-env": "^7.27.2", - "@babel/runtime": "^7.27.6", - "@react-native-community/cli": "20.0.0", - "@react-native-community/cli-platform-android": "20.0.0", - "@react-native-community/cli-platform-ios": "20.0.0", - "@react-native/babel-preset": "0.81.0", - "@react-native/metro-config": "0.81.0", - "@react-native/typescript-config": "0.81.0", - "@types/react": "^19.1.0", - "typescript": "5.8.3" - }, - "engines": { - "node": ">=18" - } + "name": "NitroImageExample", + "version": "0.10.2", + "private": true, + "scripts": { + "android": "react-native run-android", + "ios": "react-native run-ios", + "start": "react-native start --client-logs", + "pods": "bundle install && cd ios && bundle exec pod install", + "bundle-install": "bundle install", + "build:android-release": "cd android && ./gradlew assembleRelease --no-daemon" + }, + "dependencies": { + "@react-navigation/bottom-tabs": "^7.4.6", + "@react-navigation/native": "^7.1.17", + "react": "19.1.0", + "react-native": "0.81.0", + "react-native-fast-image": "^8.6.3", + "react-native-nitro-image": "../packages/react-native-nitro-image", + "react-native-nitro-web-image": "../packages/react-native-nitro-web-image", + "react-native-nitro-modules": "0.33.4", + "react-native-safe-area-context": "^5.6.0", + "react-native-screens": "^4.14.1" + }, + "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/preset-env": "^7.27.2", + "@babel/runtime": "^7.27.6", + "@react-native-community/cli": "20.0.0", + "@react-native-community/cli-platform-android": "20.0.0", + "@react-native-community/cli-platform-ios": "20.0.0", + "@react-native/babel-preset": "0.81.0", + "@react-native/metro-config": "0.81.0", + "@react-native/typescript-config": "0.81.0", + "@types/react": "^19.1.0", + "typescript": "5.8.3" + }, + "engines": { + "node": ">=18" + } } diff --git a/example/src/App.tsx b/example/src/App.tsx index 3c25ec61..2dff6a2c 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -5,24 +5,24 @@ * @format */ -import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; -import { createStaticNavigation } from "@react-navigation/native"; -import { EmptyTab } from "./EmptyTab"; -import { FastImageTab } from "./FastImageTab"; -import { NitroImageTab } from "./NitroImageTab"; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' +import { createStaticNavigation } from '@react-navigation/native' +import { EmptyTab } from './EmptyTab' +import { FastImageTab } from './FastImageTab' +import { NitroImageTab } from './NitroImageTab' const Tabs = createBottomTabNavigator({ - detachInactiveScreens: false, - screens: { - Empty: EmptyTab, - FastImage: FastImageTab, - NitroImage: NitroImageTab, - }, -}); -const Navigation = createStaticNavigation(Tabs); + detachInactiveScreens: false, + screens: { + Empty: EmptyTab, + FastImage: FastImageTab, + NitroImage: NitroImageTab, + }, +}) +const Navigation = createStaticNavigation(Tabs) function App(): React.JSX.Element { - return ; + return } -export default App; +export default App diff --git a/example/src/EmptyTab.tsx b/example/src/EmptyTab.tsx index c8a7d628..b9c619d9 100644 --- a/example/src/EmptyTab.tsx +++ b/example/src/EmptyTab.tsx @@ -1,37 +1,42 @@ -import { useState } from "react"; -import { StyleSheet, TextInput, View } from "react-native"; -import { NitroImage } from "react-native-nitro-image"; +import { useState } from 'react' +import { StyleSheet, TextInput, View } from 'react-native' +import { NitroImage } from 'react-native-nitro-image' export function EmptyTab() { - const [value, setValue] = useState('https://picsum.photos/seed/123/600') + const [value, setValue] = useState('https://picsum.photos/seed/123/600') - return ( - - - - - ); + return ( + + + + + ) } const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: "center", - alignItems: "center", - }, - text: { - fontSize: 18, - fontWeight: "500", - }, - textInput: { - borderWidth: 1, - borderRadius: 5, - paddingHorizontal: 10, - }, - image: { - width: 350, - height: 350, - backgroundColor: 'grey', - marginTop: 15 - } -}); + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + text: { + fontSize: 18, + fontWeight: '500', + }, + textInput: { + borderWidth: 1, + borderRadius: 5, + paddingHorizontal: 10, + }, + image: { + width: 350, + height: 350, + backgroundColor: 'grey', + marginTop: 15, + }, +}) diff --git a/example/src/FastImageTab.tsx b/example/src/FastImageTab.tsx index 202ee8dc..1a887724 100644 --- a/example/src/FastImageTab.tsx +++ b/example/src/FastImageTab.tsx @@ -1,29 +1,29 @@ -import { useMemo } from "react"; -import { FlatList, StyleSheet, Text, View } from "react-native"; -import FastImage from "react-native-fast-image"; -import { createImageURLs } from "./createImageURLs"; +import { useMemo } from 'react' +import { FlatList, StyleSheet, Text, View } from 'react-native' +import FastImage from 'react-native-fast-image' +import { createImageURLs } from './createImageURLs' export function FastImageTab() { - const imageURLs = useMemo(() => createImageURLs(), []); + const imageURLs = useMemo(() => createImageURLs(), []) - return ( - - FastImage Tab - ( - - )} - /> - - ); + return ( + + FastImage Tab + ( + + )} + /> + + ) } const styles = StyleSheet.create({ - image: { - width: "25%", - aspectRatio: 1, - }, -}); + image: { + width: '25%', + aspectRatio: 1, + }, +}) diff --git a/example/src/NitroImageTab.tsx b/example/src/NitroImageTab.tsx index 2dc657bd..850af923 100644 --- a/example/src/NitroImageTab.tsx +++ b/example/src/NitroImageTab.tsx @@ -1,32 +1,33 @@ -import { useMemo } from "react"; -import { FlatList, StyleSheet, Text, View } from "react-native"; -import { NitroImage } from "react-native-nitro-image"; -import { createImageURLs } from "./createImageURLs"; +import { useMemo } from 'react' +import { FlatList, StyleSheet, Text, View } from 'react-native' +import { NitroImage } from 'react-native-nitro-image' +import { createImageURLs } from './createImageURLs' export function NitroImageTab() { - const imageURLs = useMemo(() => createImageURLs(), []); + const imageURLs = useMemo(() => createImageURLs(), []) - return ( - - NitroImage Tab - ( - - )} - /> - - ); + return ( + + NitroImage Tab + ( + + )} + /> + + ) } -const styles= StyleSheet.create({ - image: { - width: '25%', - aspectRatio: 1 - } +const styles = StyleSheet.create({ + image: { + width: '25%', + aspectRatio: 1, + }, }) diff --git a/example/src/createImageURLs.ts b/example/src/createImageURLs.ts index c910e245..447135c9 100644 --- a/example/src/createImageURLs.ts +++ b/example/src/createImageURLs.ts @@ -1,10 +1,10 @@ -const DEFAULT_COUNT = 1000; +const DEFAULT_COUNT = 1000 export function createImageURLs( - count: number = DEFAULT_COUNT, - size = 800, + count: number = DEFAULT_COUNT, + size = 800, ): string[] { - return [...Array(count).fill(undefined)].map((_, index) => { - return `https://picsum.photos/seed/${index + 1}/${size}`; - }); + return [...Array(count).fill(undefined)].map((_, index) => { + return `https://picsum.photos/seed/${index + 1}/${size}` + }) } diff --git a/package.json b/package.json index f941376d..7cdb2d19 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "specs": "bun run build && bun run --cwd packages/react-native-nitro-image specs && bun run --cwd packages/react-native-nitro-web-image specs", "bootstrap": "bun i && bun run build && cd example && bundle install && bun pods", "typecheck": "bun --filter=\"**\" typecheck", - "lint": "bun image lint && bun web-image lint", - "lint-ci": "bun image lint-ci && bun web-image lint-ci", + "lint": "biome check --write", + "lint-ci": "biome check", "clean": "git clean -dfx", "release": "./scripts/release.sh", "image": "bun --cwd packages/react-native-nitro-image", @@ -24,13 +24,13 @@ "example": "bun --cwd example" }, "devDependencies": { + "@biomejs/biome": "^2.3.5", "@release-it-plugins/workspaces": "^5.0.3", "@release-it/bumper": "^7.0.5", "@release-it/conventional-changelog": "^10.0.1", "@tsconfig/react-native": "^2.0.3", "@types/jest": "^30.0.0", "@types/react": "^19.1.8", - "prettier": "^3.6.2", "react": "19.1.0", "react-native": "0.81.0", "react-native-builder-bob": "^0.37.0", diff --git a/packages/react-native-nitro-image/babel.config.js b/packages/react-native-nitro-image/babel.config.js index f8228843..3e0218e6 100644 --- a/packages/react-native-nitro-image/babel.config.js +++ b/packages/react-native-nitro-image/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: ["module:@react-native/babel-preset"], -}; + presets: ['module:@react-native/babel-preset'], +} diff --git a/packages/react-native-nitro-image/nitro.json b/packages/react-native-nitro-image/nitro.json index 5f8d861d..4a115237 100644 --- a/packages/react-native-nitro-image/nitro.json +++ b/packages/react-native-nitro-image/nitro.json @@ -1,30 +1,30 @@ { - "$schema": "https://nitro.margelo.com/nitro.schema.json", - "cxxNamespace": ["image"], - "ios": { - "iosModuleName": "NitroImage" + "$schema": "https://nitro.margelo.com/nitro.schema.json", + "cxxNamespace": ["image"], + "ios": { + "iosModuleName": "NitroImage" + }, + "android": { + "androidNamespace": ["image"], + "androidCxxLibName": "NitroImage" + }, + "autolinking": { + "ImageFactory": { + "swift": "HybridImageFactory", + "kotlin": "HybridImageFactory" }, - "android": { - "androidNamespace": ["image"], - "androidCxxLibName": "NitroImage" + "ImageLoaderFactory": { + "swift": "HybridImageLoaderFactory", + "kotlin": "HybridImageLoaderFactory" }, - "autolinking": { - "ImageFactory": { - "swift": "HybridImageFactory", - "kotlin": "HybridImageFactory" - }, - "ImageLoaderFactory": { - "swift": "HybridImageLoaderFactory", - "kotlin": "HybridImageLoaderFactory" - }, - "ImageUtils": { - "swift": "HybridImageUtils", - "kotlin": "HybridImageUtils" - }, - "NitroImageView": { - "swift": "HybridImageView", - "kotlin": "HybridImageView" - } + "ImageUtils": { + "swift": "HybridImageUtils", + "kotlin": "HybridImageUtils" }, - "ignorePaths": ["**/node_modules"] + "NitroImageView": { + "swift": "HybridImageView", + "kotlin": "HybridImageView" + } + }, + "ignorePaths": ["**/node_modules"] } diff --git a/packages/react-native-nitro-image/package.json b/packages/react-native-nitro-image/package.json index d5245e7d..a2eee7f0 100644 --- a/packages/react-native-nitro-image/package.json +++ b/packages/react-native-nitro-image/package.json @@ -1,110 +1,99 @@ { - "name": "react-native-nitro-image", - "version": "0.10.2", - "description": "A superfast in-memory Image type and view component for React Native, built with Nitro!", - "main": "lib/commonjs/index", - "module": "lib/module/index", - "types": "lib/typescript/index.d.ts", - "react-native": "src/index", - "source": "src/index", - "workspaces": [ - "example" - ], - "files": [ - "src", - "react-native.config.js", - "lib", - "nitrogen", - "android/build.gradle", - "android/gradle.properties", - "android/fix-prefab.gradle", - "android/CMakeLists.txt", - "android/src", - "ios/**/*.h", - "ios/**/*.m", - "ios/**/*.mm", - "ios/**/*.cpp", - "ios/**/*.swift", - "app.plugin.js", - "nitro.json", - "*.podspec", - "README.md" - ], - "scripts": { - "build": "rm -rf tsconfig.tsbuildinfo && rm -rf lib && bun typecheck && bob build", - "typecheck": "tsc --noEmit", - "clean": "rm -rf android/build node_modules/**/android/build lib", - "lint": "biome check . --fix", - "lint-ci": "biome check .", - "release": "release-it", - "typescript": "tsc", - "specs": "tsc && nitrogen --logLevel=\"debug\"" - }, - "keywords": [ - "react-native", - "nitro" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/mrousavy/react-native-nitro-image.git", - "directory": "packages/react-native-nitro-image" - }, - "author": "Marc Rousavy (https://github.com/mrousavy)", - "license": "MIT", - "bugs": { - "url": "https://github.com/mrousavy/react-native-nitro-image/issues" - }, - "homepage": "https://github.com/mrousavy/react-native-nitro-image#readme", - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "devDependencies": { - "@biomejs/biome": "2.2.6", - "@types/react": "^19.0.6", - "nitrogen": "0.33.4", - "react": "19.1.0", - "react-native": "0.81.0", - "react-native-nitro-modules": "0.33.4", - "typescript": "5.8.3" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-nitro-modules": "*" - }, - "prettier": { - "quoteProps": "consistent", - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5", - "useTabs": false, - "semi": false + "name": "react-native-nitro-image", + "version": "0.10.2", + "description": "A superfast in-memory Image type and view component for React Native, built with Nitro!", + "main": "lib/commonjs/index", + "module": "lib/module/index", + "types": "lib/typescript/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "workspaces": [ + "example" + ], + "files": [ + "src", + "react-native.config.js", + "lib", + "nitrogen", + "android/build.gradle", + "android/gradle.properties", + "android/fix-prefab.gradle", + "android/CMakeLists.txt", + "android/src", + "ios/**/*.h", + "ios/**/*.m", + "ios/**/*.mm", + "ios/**/*.cpp", + "ios/**/*.swift", + "app.plugin.js", + "nitro.json", + "*.podspec", + "README.md" + ], + "scripts": { + "build": "rm -rf tsconfig.tsbuildinfo && rm -rf lib && bun typecheck && bob build", + "typecheck": "tsc --noEmit", + "clean": "rm -rf android/build node_modules/**/android/build lib", + "release": "release-it", + "typescript": "tsc", + "specs": "tsc && nitrogen --logLevel=\"debug\"" + }, + "keywords": [ + "react-native", + "nitro" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/mrousavy/react-native-nitro-image.git", + "directory": "packages/react-native-nitro-image" + }, + "author": "Marc Rousavy (https://github.com/mrousavy)", + "license": "MIT", + "bugs": { + "url": "https://github.com/mrousavy/react-native-nitro-image/issues" + }, + "homepage": "https://github.com/mrousavy/react-native-nitro-image#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@types/react": "^19.0.6", + "nitrogen": "0.33.4", + "react": "19.1.0", + "react-native": "0.81.0", + "react-native-nitro-modules": "0.33.4", + "typescript": "5.8.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-nitro-modules": "*" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + }, + "release-it": { + "npm": { + "publish": true }, - "react-native-builder-bob": { - "source": "src", - "output": "lib", - "targets": [ - "commonjs", - "module", - [ - "typescript", - { - "project": "tsconfig.build.json" - } - ] - ] + "git": false, + "github": { + "release": false }, - "release-it": { - "npm": { - "publish": true - }, - "git": false, - "github": { - "release": false - }, - "hooks": { - "before:init": "bun typecheck && bun lint", - "after:bump": "bun run build" - } + "hooks": { + "before:init": "bun typecheck && bun lint", + "after:bump": "bun run build" } + } } diff --git a/packages/react-native-nitro-image/react-native.config.js b/packages/react-native-nitro-image/react-native.config.js index 13a61e95..3fdf8eaa 100644 --- a/packages/react-native-nitro-image/react-native.config.js +++ b/packages/react-native-nitro-image/react-native.config.js @@ -1,16 +1,16 @@ // https://github.com/react-native-community/cli/blob/main/docs/dependencies.md module.exports = { - dependency: { - platforms: { - /** - * @type {import('@react-native-community/cli-types').IOSDependencyParams} - */ - ios: {}, - /** - * @type {import('@react-native-community/cli-types').AndroidDependencyParams} - */ - android: {}, - }, + dependency: { + platforms: { + /** + * @type {import('@react-native-community/cli-types').IOSDependencyParams} + */ + ios: {}, + /** + * @type {import('@react-native-community/cli-types').AndroidDependencyParams} + */ + android: {}, }, -}; + }, +} diff --git a/packages/react-native-nitro-image/src/AsyncImageSource.ts b/packages/react-native-nitro-image/src/AsyncImageSource.ts index bca7a76b..3031ef6c 100644 --- a/packages/react-native-nitro-image/src/AsyncImageSource.ts +++ b/packages/react-native-nitro-image/src/AsyncImageSource.ts @@ -1,38 +1,34 @@ -import type { HybridObject } from "react-native-nitro-modules"; -import type { OptionalAsyncOptions } from "./OptionalWebLoader"; -import type { - EncodedImageData, - Image, - RawPixelData, -} from "./specs/Image.nitro"; -import type { ImageLoader } from "./specs/ImageLoader.nitro"; +import type { HybridObject } from 'react-native-nitro-modules' +import type { OptionalAsyncOptions } from './OptionalWebLoader' +import type { EncodedImageData, Image, RawPixelData } from './specs/Image.nitro' +import type { ImageLoader } from './specs/ImageLoader.nitro' -export type RequireType = number; +export type RequireType = number export type AsyncImageSource = - | Image - | ImageLoader - | { filePath: string } - | { rawPixelData: RawPixelData } - | { encodedImageData: EncodedImageData } - | { resource: string } - | { symbolName: string } - | { url: string; options?: OptionalAsyncOptions } - | RequireType; + | Image + | ImageLoader + | { filePath: string } + | { rawPixelData: RawPixelData } + | { encodedImageData: EncodedImageData } + | { resource: string } + | { symbolName: string } + | { url: string; options?: OptionalAsyncOptions } + | RequireType // @ts-expect-error i know what I'm doing export function isHybridObject(obj: T): obj is HybridObject { - // @ts-expect-error - return typeof obj === "object" && obj != null && obj.dispose != null; + // @ts-expect-error + return typeof obj === 'object' && obj != null && obj.dispose != null } // @ts-expect-error i know what I'm doing export function isHybridImage(obj: T): obj is Image { - // @ts-expect-error - return typeof obj === "object" && obj != null && obj.toRawPixelData != null; + // @ts-expect-error + return typeof obj === 'object' && obj != null && obj.toRawPixelData != null } export function isHybridImageLoader( - obj: T, - // @ts-expect-error i know what I'm doing + obj: T, + // @ts-expect-error i know what I'm doing ): obj is ImageLoader { - // @ts-expect-error - return typeof obj === "object" && obj != null && obj.loadImage != null; + // @ts-expect-error + return typeof obj === 'object' && obj != null && obj.loadImage != null } diff --git a/packages/react-native-nitro-image/src/ImageLoaders.ts b/packages/react-native-nitro-image/src/ImageLoaders.ts index 6e1303c2..0e6777f0 100644 --- a/packages/react-native-nitro-image/src/ImageLoaders.ts +++ b/packages/react-native-nitro-image/src/ImageLoaders.ts @@ -1,8 +1,8 @@ -import { NitroModules } from "react-native-nitro-modules"; -import type { ImageLoaderFactory } from "./specs/ImageLoaderFactory.nitro"; +import { NitroModules } from 'react-native-nitro-modules' +import type { ImageLoaderFactory } from './specs/ImageLoaderFactory.nitro' /** * A factory for creating `ImageLoader` instances. */ export const ImageLoaders = - NitroModules.createHybridObject("ImageLoaderFactory"); + NitroModules.createHybridObject('ImageLoaderFactory') diff --git a/packages/react-native-nitro-image/src/ImageUtils.ts b/packages/react-native-nitro-image/src/ImageUtils.ts index d221478b..5280e1a3 100644 --- a/packages/react-native-nitro-image/src/ImageUtils.ts +++ b/packages/react-native-nitro-image/src/ImageUtils.ts @@ -1,21 +1,20 @@ -import { NitroModules } from "react-native-nitro-modules"; -import type { ImageUtils } from "./specs/ImageUtils.nitro"; +import { NitroModules } from 'react-native-nitro-modules' +import type { ImageUtils } from './specs/ImageUtils.nitro' -const utils = NitroModules.createHybridObject("ImageUtils"); +const utils = NitroModules.createHybridObject('ImageUtils') /** * Returns `true` when the host platform supports loading Images * in `HEIC` format. */ -export const supportsHeicLoading = utils.supportsHeicLoading; +export const supportsHeicLoading = utils.supportsHeicLoading /** * Returns `true` when the host platform supports writing Images * in `HEIC` format. */ -export const supportsHeicWriting = utils.supportsHeicWriting; +export const supportsHeicWriting = utils.supportsHeicWriting -export const thumbHashToBase64String = - utils.thumbHashToBase64String.bind(utils); +export const thumbHashToBase64String = utils.thumbHashToBase64String.bind(utils) export const thumbHashFromBase64String = - utils.thumbhashFromBase64String.bind(utils); + utils.thumbhashFromBase64String.bind(utils) diff --git a/packages/react-native-nitro-image/src/Images.ts b/packages/react-native-nitro-image/src/Images.ts index 22aa3a50..b3430d82 100644 --- a/packages/react-native-nitro-image/src/Images.ts +++ b/packages/react-native-nitro-image/src/Images.ts @@ -1,8 +1,8 @@ -import { NitroModules } from "react-native-nitro-modules"; -import type { ImageFactory } from "./specs/ImageFactory.nitro"; +import { NitroModules } from 'react-native-nitro-modules' +import type { ImageFactory } from './specs/ImageFactory.nitro' /** * A factory for loading and creating `Image` instances. */ export const Images = - NitroModules.createHybridObject("ImageFactory"); + NitroModules.createHybridObject('ImageFactory') diff --git a/packages/react-native-nitro-image/src/NativeNitroImage.tsx b/packages/react-native-nitro-image/src/NativeNitroImage.tsx index 204bfba5..c0fcdd06 100644 --- a/packages/react-native-nitro-image/src/NativeNitroImage.tsx +++ b/packages/react-native-nitro-image/src/NativeNitroImage.tsx @@ -1,9 +1,9 @@ -import { getHostComponent } from "react-native-nitro-modules"; -import ViewConfig from "../nitrogen/generated/shared/json/NitroImageViewConfig.json"; +import { getHostComponent } from 'react-native-nitro-modules' +import ViewConfig from '../nitrogen/generated/shared/json/NitroImageViewConfig.json' import type { - NativeNitroImageViewMethods, - NativeNitroImageViewProps, -} from "./specs/ImageView.nitro"; + NativeNitroImageViewMethods, + NativeNitroImageViewProps, +} from './specs/ImageView.nitro' /** * The native renderable `` view. @@ -16,6 +16,6 @@ import type { * ``` */ export const NativeNitroImage = getHostComponent< - NativeNitroImageViewProps, - NativeNitroImageViewMethods ->("NitroImageView", () => ViewConfig); + NativeNitroImageViewProps, + NativeNitroImageViewMethods +>('NitroImageView', () => ViewConfig) diff --git a/packages/react-native-nitro-image/src/NitroImage.tsx b/packages/react-native-nitro-image/src/NitroImage.tsx index 953da659..004ad210 100644 --- a/packages/react-native-nitro-image/src/NitroImage.tsx +++ b/packages/react-native-nitro-image/src/NitroImage.tsx @@ -1,15 +1,15 @@ // biome-ignore lint/correctness/noUnusedImports: Needed for JSX runtime -import React from "react"; -import type { HostComponent } from "react-native"; -import type { AsyncImageSource } from "./AsyncImageSource"; -import { NativeNitroImage } from "./NativeNitroImage"; -import { useImageLoader } from "./useImageLoader"; +import React from 'react' +import type { HostComponent } from 'react-native' +import type { AsyncImageSource } from './AsyncImageSource' +import { NativeNitroImage } from './NativeNitroImage' +import { useImageLoader } from './useImageLoader' -type ReactProps = T extends HostComponent ? P : never; -type NativeImageProps = ReactProps; +type ReactProps = T extends HostComponent ? P : never +type NativeImageProps = ReactProps -export interface NitroImageProps extends Omit { - image: AsyncImageSource; +export interface NitroImageProps extends Omit { + image: AsyncImageSource } /** @@ -31,6 +31,6 @@ export interface NitroImageProps extends Omit { * ``` */ export function NitroImage({ image, ...props }: NitroImageProps) { - const actualImage = useImageLoader(image); - return ; + const actualImage = useImageLoader(image) + return } diff --git a/packages/react-native-nitro-image/src/OptionalWebLoader.ts b/packages/react-native-nitro-image/src/OptionalWebLoader.ts index bc4e4b2c..cd9c2c82 100644 --- a/packages/react-native-nitro-image/src/OptionalWebLoader.ts +++ b/packages/react-native-nitro-image/src/OptionalWebLoader.ts @@ -1,36 +1,36 @@ // biome-ignore lint/suspicious/noTsIgnore: Type Compilation is a race-condition // @ts-ignore -type WebImagesType = typeof import("react-native-nitro-web-image")["WebImages"]; +type WebImagesType = typeof import('react-native-nitro-web-image')['WebImages'] type OptionalWebImagesType = Pick< - WebImagesType, - "createWebImageLoader" | "loadFromURLAsync" ->; + WebImagesType, + 'createWebImageLoader' | 'loadFromURLAsync' +> -let createWebImageLoader: WebImagesType["createWebImageLoader"] = () => { - throw new Error( - `Web Images are not supported because react-native-nitro-web-image is not installed!`, - ); -}; -let loadFromURLAsync: WebImagesType["loadFromURLAsync"] = () => { - throw new Error( - `Web Images are not supported because react-native-nitro-web-image is not installed!`, - ); -}; +let createWebImageLoader: WebImagesType['createWebImageLoader'] = () => { + throw new Error( + `Web Images are not supported because react-native-nitro-web-image is not installed!`, + ) +} +let loadFromURLAsync: WebImagesType['loadFromURLAsync'] = () => { + throw new Error( + `Web Images are not supported because react-native-nitro-web-image is not installed!`, + ) +} export type OptionalAsyncOptions = Parameters< - WebImagesType["loadFromURLAsync"] ->[1]; + WebImagesType['loadFromURLAsync'] +>[1] try { - const WebImages = require("react-native-nitro-web-image") - .WebImages as WebImagesType; - createWebImageLoader = WebImages.createWebImageLoader.bind(WebImages); - loadFromURLAsync = WebImages.loadFromURLAsync.bind(WebImages); + const WebImages = require('react-native-nitro-web-image') + .WebImages as WebImagesType + createWebImageLoader = WebImages.createWebImageLoader.bind(WebImages) + loadFromURLAsync = WebImages.loadFromURLAsync.bind(WebImages) } catch { - // react-native-nitro-web-image is not installed, so only local images are supported. + // react-native-nitro-web-image is not installed, so only local images are supported. } export const OptionalWebImages: OptionalWebImagesType = { - createWebImageLoader, - loadFromURLAsync, -}; + createWebImageLoader, + loadFromURLAsync, +} diff --git a/packages/react-native-nitro-image/src/createImageLoader.ts b/packages/react-native-nitro-image/src/createImageLoader.ts index 12d2e8b2..dffa1fc8 100644 --- a/packages/react-native-nitro-image/src/createImageLoader.ts +++ b/packages/react-native-nitro-image/src/createImageLoader.ts @@ -1,52 +1,49 @@ -import { Image as RNImage } from "react-native"; +import { Image as RNImage } from 'react-native' import { - type AsyncImageSource, - isHybridImage, - isHybridImageLoader, -} from "./AsyncImageSource"; -import { ImageLoaders } from "./ImageLoaders"; -import { OptionalWebImages } from "./OptionalWebLoader"; -import type { Image } from "./specs/Image.nitro"; -import type { ImageLoader } from "./specs/ImageLoader.nitro"; + type AsyncImageSource, + isHybridImage, + isHybridImageLoader, +} from './AsyncImageSource' +import { ImageLoaders } from './ImageLoaders' +import { OptionalWebImages } from './OptionalWebLoader' +import type { Image } from './specs/Image.nitro' +import type { ImageLoader } from './specs/ImageLoader.nitro' export function createImageLoader( - source: AsyncImageSource, + source: AsyncImageSource, ): ImageLoader | Image { - if (typeof source === "number") { - // It's a require(...) - a `number` which we need to resolve first - const resolvedSource = RNImage.resolveAssetSource(source); - if (resolvedSource.uri.startsWith("http")) { - // In debug, assets are streamed over the network - return createImageLoader({ url: resolvedSource.uri }); - } else if (resolvedSource.uri.startsWith("file")) { - // In release, assets are embedded files... - return createImageLoader({ filePath: resolvedSource.uri }); - } else { - // ...or resource IDs - return createImageLoader({ resource: resolvedSource.uri }); - } - } else if (isHybridImage(source)) { - return source; - } else if (isHybridImageLoader(source)) { - return source; - } else if ("filePath" in source) { - return ImageLoaders.createFileImageLoader(source.filePath); - } else if ("encodedImageData" in source) { - return ImageLoaders.createEncodedImageDataImageLoader( - source.encodedImageData, - ); - } else if ("rawPixelData" in source) { - return ImageLoaders.createRawPixelDataImageLoader(source.rawPixelData); - } else if ("resource" in source) { - return ImageLoaders.createResourceImageLoader(source.resource); - } else if ("symbolName" in source) { - return ImageLoaders.createSymbolImageLoader(source.symbolName); - } else if ("url" in source) { - return OptionalWebImages.createWebImageLoader( - source.url, - source.options, - ); + if (typeof source === 'number') { + // It's a require(...) - a `number` which we need to resolve first + const resolvedSource = RNImage.resolveAssetSource(source) + if (resolvedSource.uri.startsWith('http')) { + // In debug, assets are streamed over the network + return createImageLoader({ url: resolvedSource.uri }) + } else if (resolvedSource.uri.startsWith('file')) { + // In release, assets are embedded files... + return createImageLoader({ filePath: resolvedSource.uri }) } else { - throw new Error(`Unknown Image source! ${JSON.stringify(source)}`); + // ...or resource IDs + return createImageLoader({ resource: resolvedSource.uri }) } + } else if (isHybridImage(source)) { + return source + } else if (isHybridImageLoader(source)) { + return source + } else if ('filePath' in source) { + return ImageLoaders.createFileImageLoader(source.filePath) + } else if ('encodedImageData' in source) { + return ImageLoaders.createEncodedImageDataImageLoader( + source.encodedImageData, + ) + } else if ('rawPixelData' in source) { + return ImageLoaders.createRawPixelDataImageLoader(source.rawPixelData) + } else if ('resource' in source) { + return ImageLoaders.createResourceImageLoader(source.resource) + } else if ('symbolName' in source) { + return ImageLoaders.createSymbolImageLoader(source.symbolName) + } else if ('url' in source) { + return OptionalWebImages.createWebImageLoader(source.url, source.options) + } else { + throw new Error(`Unknown Image source! ${JSON.stringify(source)}`) + } } diff --git a/packages/react-native-nitro-image/src/index.ts b/packages/react-native-nitro-image/src/index.ts index a5e135c0..3f8eb89c 100644 --- a/packages/react-native-nitro-image/src/index.ts +++ b/packages/react-native-nitro-image/src/index.ts @@ -1,11 +1,11 @@ -export * from "./createImageLoader"; -export * from "./Images"; -export * from "./ImageUtils"; -export * from "./loadImage"; -export { NativeNitroImage } from "./NativeNitroImage"; -export { NitroImage, type NitroImageProps } from "./NitroImage"; -export type { Image } from "./specs/Image.nitro"; -export type { ImageLoader } from "./specs/ImageLoader.nitro"; +export * from './createImageLoader' +export * from './Images' +export * from './ImageUtils' +export * from './loadImage' +export { NativeNitroImage } from './NativeNitroImage' +export { NitroImage, type NitroImageProps } from './NitroImage' +export type { Image } from './specs/Image.nitro' +export type { ImageLoader } from './specs/ImageLoader.nitro' -export * from "./useImage"; -export * from "./useImageLoader"; +export * from './useImage' +export * from './useImageLoader' diff --git a/packages/react-native-nitro-image/src/loadImage.ts b/packages/react-native-nitro-image/src/loadImage.ts index abb619ff..679c7023 100644 --- a/packages/react-native-nitro-image/src/loadImage.ts +++ b/packages/react-native-nitro-image/src/loadImage.ts @@ -1,52 +1,52 @@ -import { Image as RNImage } from "react-native"; +import { Image as RNImage } from 'react-native' import { - type AsyncImageSource, - isHybridImage, - isHybridImageLoader, -} from "./AsyncImageSource"; -import { Images } from "./Images"; -import { OptionalWebImages } from "./OptionalWebLoader"; -import type { Image } from "./specs/Image.nitro"; + type AsyncImageSource, + isHybridImage, + isHybridImageLoader, +} from './AsyncImageSource' +import { Images } from './Images' +import { OptionalWebImages } from './OptionalWebLoader' +import type { Image } from './specs/Image.nitro' export function loadImage(source: AsyncImageSource): Promise | Image { - if (typeof source === "number") { - // It's a require(...) - a `number` which we need to resolve first - const resolvedSource = RNImage.resolveAssetSource(source); - if (resolvedSource.uri.startsWith("http")) { - // In debug, assets are streamed over the network - return loadImage({ url: resolvedSource.uri }); - } else if (resolvedSource.uri.startsWith("file")) { - // In release, assets are embedded files... - return loadImage({ filePath: resolvedSource.uri }); - } else { - // ...or resource IDs - return loadImage({ resource: resolvedSource.uri }); - } - } else if (isHybridImage(source)) { - // don't do anything if this already is a HybridImage - return source; - } else if (isHybridImageLoader(source)) { - // It's an ImageLoader - return source.loadImage(); - } else if ("filePath" in source) { - // It's a { filePath } - return Images.loadFromFileAsync(source.filePath); - } else if ("encodedImageData" in source) { - // It's a { encodedImageData } - return Images.loadFromEncodedImageDataAsync(source.encodedImageData); - } else if ("rawPixelData" in source) { - // It's a { rawPixelData } - return Images.loadFromRawPixelDataAsync(source.rawPixelData); - } else if ("resource" in source) { - // It's a { resource } - return Images.loadFromResourcesAsync(source.resource); - } else if ("symbolName" in source) { - // It's a { symbolName } - return Promise.resolve(Images.loadFromSymbol(source.symbolName)); - } else if ("url" in source) { - // It's a { url } - return OptionalWebImages.loadFromURLAsync(source.url, source.options); + if (typeof source === 'number') { + // It's a require(...) - a `number` which we need to resolve first + const resolvedSource = RNImage.resolveAssetSource(source) + if (resolvedSource.uri.startsWith('http')) { + // In debug, assets are streamed over the network + return loadImage({ url: resolvedSource.uri }) + } else if (resolvedSource.uri.startsWith('file')) { + // In release, assets are embedded files... + return loadImage({ filePath: resolvedSource.uri }) } else { - throw new Error(`Unknown Image source! ${JSON.stringify(source)}`); + // ...or resource IDs + return loadImage({ resource: resolvedSource.uri }) } + } else if (isHybridImage(source)) { + // don't do anything if this already is a HybridImage + return source + } else if (isHybridImageLoader(source)) { + // It's an ImageLoader + return source.loadImage() + } else if ('filePath' in source) { + // It's a { filePath } + return Images.loadFromFileAsync(source.filePath) + } else if ('encodedImageData' in source) { + // It's a { encodedImageData } + return Images.loadFromEncodedImageDataAsync(source.encodedImageData) + } else if ('rawPixelData' in source) { + // It's a { rawPixelData } + return Images.loadFromRawPixelDataAsync(source.rawPixelData) + } else if ('resource' in source) { + // It's a { resource } + return Images.loadFromResourcesAsync(source.resource) + } else if ('symbolName' in source) { + // It's a { symbolName } + return Promise.resolve(Images.loadFromSymbol(source.symbolName)) + } else if ('url' in source) { + // It's a { url } + return OptionalWebImages.loadFromURLAsync(source.url, source.options) + } else { + throw new Error(`Unknown Image source! ${JSON.stringify(source)}`) + } } diff --git a/packages/react-native-nitro-image/src/markHybridObject.ts b/packages/react-native-nitro-image/src/markHybridObject.ts index 7dc8789a..dd3d7d2f 100644 --- a/packages/react-native-nitro-image/src/markHybridObject.ts +++ b/packages/react-native-nitro-image/src/markHybridObject.ts @@ -1,27 +1,27 @@ -import { type AsyncImageSource, isHybridObject } from "./AsyncImageSource"; -import type { Image } from "./specs/Image.nitro"; -import type { ImageLoader } from "./specs/ImageLoader.nitro"; +import { type AsyncImageSource, isHybridObject } from './AsyncImageSource' +import type { Image } from './specs/Image.nitro' +import type { ImageLoader } from './specs/ImageLoader.nitro' -let counter = 0; +let counter = 0 export function markHybridObject( - object: Image | ImageLoader, - source: AsyncImageSource, + object: Image | ImageLoader, + source: AsyncImageSource, ): typeof object { - if (isHybridObject(source)) { - // `source` is a HybridObject - to avoid recursion, we just set it to an incrementing counter. - Object.defineProperty(object, "__source", { - enumerable: true, - configurable: true, - value: counter, - }); - counter++; - } else { - // `source` is just an input object, we can use it to tag the Image properly - Object.defineProperty(object, "__source", { - enumerable: true, - configurable: true, - value: source, - }); - } - return object; + if (isHybridObject(source)) { + // `source` is a HybridObject - to avoid recursion, we just set it to an incrementing counter. + Object.defineProperty(object, '__source', { + enumerable: true, + configurable: true, + value: counter, + }) + counter++ + } else { + // `source` is just an input object, we can use it to tag the Image properly + Object.defineProperty(object, '__source', { + enumerable: true, + configurable: true, + value: source, + }) + } + return object } diff --git a/packages/react-native-nitro-image/src/specs/Image.nitro.ts b/packages/react-native-nitro-image/src/specs/Image.nitro.ts index 3e24c7b0..5433e980 100644 --- a/packages/react-native-nitro-image/src/specs/Image.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/Image.nitro.ts @@ -1,4 +1,4 @@ -import type { HybridObject } from "react-native-nitro-modules"; +import type { HybridObject } from 'react-native-nitro-modules' /** * Represents the pixel ordering format of the literal bytes in memory. @@ -23,201 +23,198 @@ import type { HybridObject } from "react-native-nitro-modules"; * `[B, G, R, A]` instead. This is why a `Bitmap` that is in `ARGB_8888` config will return `BGRA` here in Nitro Image. */ export type PixelFormat = - | "ARGB" - | "BGRA" - | "ABGR" - | "RGBA" - | "XRGB" - | "BGRX" - | "XBGR" - | "RGBX" - | "RGB" - | "BGR" - | "unknown"; + | 'ARGB' + | 'BGRA' + | 'ABGR' + | 'RGBA' + | 'XRGB' + | 'BGRX' + | 'XBGR' + | 'RGBX' + | 'RGB' + | 'BGR' + | 'unknown' /** * Describes the format of an encoded Image. */ -export type ImageFormat = "jpg" | "png" | "heic"; +export type ImageFormat = 'jpg' | 'png' | 'heic' /** * Describes raw pixel data (`buffer`) with `width`, `height` and `pixelFormat`. */ export interface RawPixelData { - buffer: ArrayBuffer; - width: number; - height: number; - pixelFormat: PixelFormat; + buffer: ArrayBuffer + width: number + height: number + pixelFormat: PixelFormat } /** * Describes encoded image data (`buffer`) with `width`, `height` and `imageFormat`. */ export interface EncodedImageData { - buffer: ArrayBuffer; - width: number; - height: number; - imageFormat: ImageFormat; + buffer: ArrayBuffer + width: number + height: number + imageFormat: ImageFormat } /** * A native Image instance. */ export interface Image - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - readonly width: number; - readonly height: number; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + readonly width: number + readonly height: number - /** - * Returns an {@linkcode ArrayBuffer} containing the raw pixel data of the Image. - * @note Raw pixel data is either in {@linkcode PixelFormat | 'ARGB'} or - * {@linkcode PixelFormat | 'BGRA'} format, depending on the OS' endianess. - * @param allowGpu If `allowGpu` is set to `true`, the returned buffer might - * be a `HardwareBuffer` (a GPU-buffer) on Android. By default, it is `false`. - * @example - * ```ts - * const rawData = image.toRawArrayBuffer() - * const data = new Uint8Array(rawData.buffer) - * let r, g, b - * if (rawData.pixelFormat === 'bgra') { - * r = data[2] - * g = data[1] - * b = data[0] - * } else { - * r = data[0] - * g = data[1] - * b = data[2] - * } - * ``` - */ - toRawPixelData(allowGpu?: boolean): RawPixelData; - toRawPixelDataAsync(allowGpu?: boolean): Promise; + /** + * Returns an {@linkcode ArrayBuffer} containing the raw pixel data of the Image. + * @note Raw pixel data is either in {@linkcode PixelFormat | 'ARGB'} or + * {@linkcode PixelFormat | 'BGRA'} format, depending on the OS' endianess. + * @param allowGpu If `allowGpu` is set to `true`, the returned buffer might + * be a `HardwareBuffer` (a GPU-buffer) on Android. By default, it is `false`. + * @example + * ```ts + * const rawData = image.toRawArrayBuffer() + * const data = new Uint8Array(rawData.buffer) + * let r, g, b + * if (rawData.pixelFormat === 'bgra') { + * r = data[2] + * g = data[1] + * b = data[0] + * } else { + * r = data[0] + * g = data[1] + * b = data[2] + * } + * ``` + */ + toRawPixelData(allowGpu?: boolean): RawPixelData + toRawPixelDataAsync(allowGpu?: boolean): Promise - /** - * Returns an {@linkcode ArrayBuffer} containing the encoded data of an Image in - * the requested container {@linkcode format}. - * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use - * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the - * {@linkcode quality} flag is ignored. - * @example - * ```ts - * const compressed = image.toEncodedImageData('jpg', 70) - * ``` - */ - toEncodedImageData(format: ImageFormat, quality?: number): EncodedImageData; - toEncodedImageDataAsync( - format: ImageFormat, - quality?: number, - ): Promise; + /** + * Returns an {@linkcode ArrayBuffer} containing the encoded data of an Image in + * the requested container {@linkcode format}. + * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use + * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the + * {@linkcode quality} flag is ignored. + * @example + * ```ts + * const compressed = image.toEncodedImageData('jpg', 70) + * ``` + */ + toEncodedImageData(format: ImageFormat, quality?: number): EncodedImageData + toEncodedImageDataAsync( + format: ImageFormat, + quality?: number, + ): Promise - /** - * Resizes this Image into a new image with the new given {@linkcode width} and {@linkcode height}. - * @example - * ```ts - * const smaller = image.resize(image.width / 2, image.height / 2) - * ``` - */ - resize(width: number, height: number): Image; - resizeAsync(width: number, height: number): Promise; + /** + * Resizes this Image into a new image with the new given {@linkcode width} and {@linkcode height}. + * @example + * ```ts + * const smaller = image.resize(image.width / 2, image.height / 2) + * ``` + */ + resize(width: number, height: number): Image + resizeAsync(width: number, height: number): Promise - /** - * Rotates this Image by the given {@linkcode degrees} and returns - * the newly created {@linkcode Image}. - * - * @param degrees The degrees to rotate the Image. May be any arbitrary number, and can be negative. - * @param allowFastFlagRotation When {@linkcode allowFastFlagRotation} is set to `true`, the implementation may choose to only change the orientation flag on the underying image instead of physicaly rotating the buffers. This may only work when {@linkcode degrees} is a multiple of `90`, and will only apply rotation when displaying the Image (via view transforms) or exporting it to a file (via EXIF flags). The actual buffer (e.g. obtained via {@linkcode toRawPixelData | toRawPixelData()}) may remain untouched. - * @example - * ```ts - * const upsideDown = image.rotate(180) - * ``` - */ - rotate(degrees: number, allowFastFlagRotation?: boolean): Image; - rotateAsync( - degrees: number, - allowFastFlagRotation?: boolean, - ): Promise; + /** + * Rotates this Image by the given {@linkcode degrees} and returns + * the newly created {@linkcode Image}. + * + * @param degrees The degrees to rotate the Image. May be any arbitrary number, and can be negative. + * @param allowFastFlagRotation When {@linkcode allowFastFlagRotation} is set to `true`, the implementation may choose to only change the orientation flag on the underying image instead of physicaly rotating the buffers. This may only work when {@linkcode degrees} is a multiple of `90`, and will only apply rotation when displaying the Image (via view transforms) or exporting it to a file (via EXIF flags). The actual buffer (e.g. obtained via {@linkcode toRawPixelData | toRawPixelData()}) may remain untouched. + * @example + * ```ts + * const upsideDown = image.rotate(180) + * ``` + */ + rotate(degrees: number, allowFastFlagRotation?: boolean): Image + rotateAsync(degrees: number, allowFastFlagRotation?: boolean): Promise - /** - * Crops this Image into a new image starting from the source image's {@linkcode startX} and {@linkcode startY} coordinates, - * up until the source image's {@linkcode endX} and {@linkcode endY} coordinates. - * @example - * ```ts - * const cropped = image.crop( - * image.width * 0.1, - * image.height * 0.1, - * image.width * 0.8, - * image.height * 0.8 - * ) - * ``` - */ - crop(startX: number, startY: number, endX: number, endY: number): Image; - cropAsync( - startX: number, - startY: number, - endX: number, - endY: number, - ): Promise; + /** + * Crops this Image into a new image starting from the source image's {@linkcode startX} and {@linkcode startY} coordinates, + * up until the source image's {@linkcode endX} and {@linkcode endY} coordinates. + * @example + * ```ts + * const cropped = image.crop( + * image.width * 0.1, + * image.height * 0.1, + * image.width * 0.8, + * image.height * 0.8 + * ) + * ``` + */ + crop(startX: number, startY: number, endX: number, endY: number): Image + cropAsync( + startX: number, + startY: number, + endX: number, + endY: number, + ): Promise - /** - * Saves this image in the given {@linkcode ImageFormat} to the given {@linkcode path}. - * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use - * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the - * {@linkcode quality} flag is ignored. - * @example - * ```ts - * await image.saveToFileAsync(path, 'jpg', 80) - * ``` - */ - saveToFileAsync( - path: string, - format: ImageFormat, - quality?: number, - ): Promise; - /** - * Saves this image in the given {@linkcode ImageFormat} to a temporary file, and return it's path. - * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use - * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the - * {@linkcode quality} flag is ignored. - * @example - * ```ts - * const path = await image.saveToTemporaryFileAsync('jpg', 80) - * ``` - */ - saveToTemporaryFileAsync( - format: ImageFormat, - quality?: number, - ): Promise; + /** + * Saves this image in the given {@linkcode ImageFormat} to the given {@linkcode path}. + * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use + * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the + * {@linkcode quality} flag is ignored. + * @example + * ```ts + * await image.saveToFileAsync(path, 'jpg', 80) + * ``` + */ + saveToFileAsync( + path: string, + format: ImageFormat, + quality?: number, + ): Promise + /** + * Saves this image in the given {@linkcode ImageFormat} to a temporary file, and return it's path. + * @note If the requested {@linkcode format} is {@linkcode ImageFormat | 'jpg'}, you can use + * {@linkcode quality} to compress the image. Quality ranges from 0(most)...100(least). In {@linkcode ImageFormat | 'png'}, the + * {@linkcode quality} flag is ignored. + * @example + * ```ts + * const path = await image.saveToTemporaryFileAsync('jpg', 80) + * ``` + */ + saveToTemporaryFileAsync( + format: ImageFormat, + quality?: number, + ): Promise - /** - * Encodes this Image into a ThumbHash. - * To convert the returned ThumbHash to a string, use `thumbHashToBase64String(...)`. - * @note To keep this efficient, {@linkcode resize} this image to a small size (<100x100) first. - * @example - * ```ts - * const small = image.resize(100, 100) - * const thumbHash = small.toThumbHash() - * ``` - */ - toThumbHash(): ArrayBuffer; - toThumbHashAsync(): Promise; + /** + * Encodes this Image into a ThumbHash. + * To convert the returned ThumbHash to a string, use `thumbHashToBase64String(...)`. + * @note To keep this efficient, {@linkcode resize} this image to a small size (<100x100) first. + * @example + * ```ts + * const small = image.resize(100, 100) + * const thumbHash = small.toThumbHash() + * ``` + */ + toThumbHash(): ArrayBuffer + toThumbHashAsync(): Promise - /** - * Renders the given {@linkcode Image} into a copy of this {@linkcode Image}, - * at the given {@linkcode x} and {@linkcode y} position, scaled to the - * given {@linkcode width} and {@linkcode height}. - */ - renderInto( - image: Image, - x: number, - y: number, - width: number, - height: number, - ): Image; - renderIntoAsync( - image: Image, - x: number, - y: number, - width: number, - height: number, - ): Promise; + /** + * Renders the given {@linkcode Image} into a copy of this {@linkcode Image}, + * at the given {@linkcode x} and {@linkcode y} position, scaled to the + * given {@linkcode width} and {@linkcode height}. + */ + renderInto( + image: Image, + x: number, + y: number, + width: number, + height: number, + ): Image + renderIntoAsync( + image: Image, + x: number, + y: number, + width: number, + height: number, + ): Promise } diff --git a/packages/react-native-nitro-image/src/specs/ImageFactory.nitro.ts b/packages/react-native-nitro-image/src/specs/ImageFactory.nitro.ts index a1de18fe..adfbaeaf 100644 --- a/packages/react-native-nitro-image/src/specs/ImageFactory.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/ImageFactory.nitro.ts @@ -1,5 +1,5 @@ -import type { HybridObject } from "react-native-nitro-modules"; -import type { EncodedImageData, Image, RawPixelData } from "./Image.nitro"; +import type { HybridObject } from 'react-native-nitro-modules' +import type { EncodedImageData, Image, RawPixelData } from './Image.nitro' /** * Represents a Color in an {@linkcode Image}. @@ -10,121 +10,121 @@ import type { EncodedImageData, Image, RawPixelData } from "./Image.nitro"; * If the {@linkcode Image} has no alpha channel, this value is ignored. */ export interface Color { - r: number; - g: number; - b: number; - a?: number; + r: number + g: number + b: number + a?: number } export interface ImageFactory - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - /** - * Synchronously creates a new blank {@linkcode Image} of the given size. - * @param width The width of the new Image - * @param height The height of the new Image - * @param enableAlpha Whether to add an alpha channel for transparency - * @param fill If set, fill the whole image with the given color - */ - createBlankImage( - width: number, - height: number, - enableAlpha: boolean, - fill?: Color, - ): Image; - /** - * Asynchronously creates a new blank {@linkcode Image} of the given size. - * @param width The width of the new Image - * @param height The height of the new Image - * @param enableAlpha Whether to add an alpha channel for transparency - * @param fill If set, fill the whole image with the given color - */ - createBlankImageAsync( - width: number, - height: number, - enableAlpha: boolean, - fill?: Color, - ): Promise; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Synchronously creates a new blank {@linkcode Image} of the given size. + * @param width The width of the new Image + * @param height The height of the new Image + * @param enableAlpha Whether to add an alpha channel for transparency + * @param fill If set, fill the whole image with the given color + */ + createBlankImage( + width: number, + height: number, + enableAlpha: boolean, + fill?: Color, + ): Image + /** + * Asynchronously creates a new blank {@linkcode Image} of the given size. + * @param width The width of the new Image + * @param height The height of the new Image + * @param enableAlpha Whether to add an alpha channel for transparency + * @param fill If set, fill the whole image with the given color + */ + createBlankImageAsync( + width: number, + height: number, + enableAlpha: boolean, + fill?: Color, + ): Promise - /** - * Synchronously loads an {@linkcode Image} from the given {@linkcode filePath}. - * @param filePath The file path of the {@linkcode Image}. Must contain a file extension. - * @throws If the {@linkcode filePath} is invalid. - * @throws If the data at the given {@linkcode filePath} cannot be parsed as an {@linkcode Image}. - */ - loadFromFile(filePath: string): Image; - /** - * Asynchronously loads an {@linkcode Image} from the given {@linkcode filePath}. - * @param filePath The file path of the {@linkcode Image}. Must contain a file extension. - * @throws If the {@linkcode filePath} is invalid. - * @throws If the data at the given {@linkcode filePath} cannot be parsed as an {@linkcode Image}. - */ - loadFromFileAsync(filePath: string): Promise; + /** + * Synchronously loads an {@linkcode Image} from the given {@linkcode filePath}. + * @param filePath The file path of the {@linkcode Image}. Must contain a file extension. + * @throws If the {@linkcode filePath} is invalid. + * @throws If the data at the given {@linkcode filePath} cannot be parsed as an {@linkcode Image}. + */ + loadFromFile(filePath: string): Image + /** + * Asynchronously loads an {@linkcode Image} from the given {@linkcode filePath}. + * @param filePath The file path of the {@linkcode Image}. Must contain a file extension. + * @throws If the {@linkcode filePath} is invalid. + * @throws If the data at the given {@linkcode filePath} cannot be parsed as an {@linkcode Image}. + */ + loadFromFileAsync(filePath: string): Promise - /** - * Synchronously loads an {@linkcode Image} from the given resource-/system-name. - * @param name The resource-/system-name of the image to load. - * @throws If no {@linkcode Image} exists under the given {@linkcode name}. - * @throws If the file under the given {@linkcode name} cannot be parsed as an {@linkcode Image}. - */ - loadFromResources(name: string): Image; - /** - * Asynchronously loads an {@linkcode Image} from the given resource-/system-name. - * @param name The resource-/system-name of the image to load. - * @throws If no {@linkcode Image} exists under the given {@linkcode name}. - * @throws If the file under the given {@linkcode name} cannot be parsed as an {@linkcode Image}. - */ - loadFromResourcesAsync(name: string): Promise; + /** + * Synchronously loads an {@linkcode Image} from the given resource-/system-name. + * @param name The resource-/system-name of the image to load. + * @throws If no {@linkcode Image} exists under the given {@linkcode name}. + * @throws If the file under the given {@linkcode name} cannot be parsed as an {@linkcode Image}. + */ + loadFromResources(name: string): Image + /** + * Asynchronously loads an {@linkcode Image} from the given resource-/system-name. + * @param name The resource-/system-name of the image to load. + * @throws If no {@linkcode Image} exists under the given {@linkcode name}. + * @throws If the file under the given {@linkcode name} cannot be parsed as an {@linkcode Image}. + */ + loadFromResourcesAsync(name: string): Promise - /** - * Synchronously loads an {@linkcode Image} from the given symbol name. - * This is iOS only! - * @param symbolName The symbol name of the image to load. On iOS, this is the SF Symbols Name. - * @throws If no {@linkcode Image} symbol exists under the given {@linkcode symbolName}. - * @platform iOS 13 - */ - loadFromSymbol(symbolName: string): Image; + /** + * Synchronously loads an {@linkcode Image} from the given symbol name. + * This is iOS only! + * @param symbolName The symbol name of the image to load. On iOS, this is the SF Symbols Name. + * @throws If no {@linkcode Image} symbol exists under the given {@linkcode symbolName}. + * @platform iOS 13 + */ + loadFromSymbol(symbolName: string): Image - /** - * Synchronously loads an {@linkcode Image} from the given {@linkcode RawPixelData}'s {@linkcode ArrayBuffer}. - * @param data The {@linkcode RawPixelData} object carrying the **raw** RGB image data and describing it's format. - * @param allowGpu If `allowGpu` is set to `true` and the given {@linkcode data} is a GPU-buffer, the {@linkcode Image} - * might be wrapping the given GPU-buffer without performing a copy. By default, `allowGpu` is `false` - * @throws If the given {@linkcode RawPixelData} is not a valid RGB buffer representing an {@linkcode Image}. - * @note The given pixel data has to have pre-multiplied alpha, and be some kind of RGB format with 4-bytes-per-pixel. - */ - loadFromRawPixelData(data: RawPixelData, allowGpu?: boolean): Image; - /** - * Asynchronously loads an {@linkcode Image} from the given {@linkcode RawPixelData}'s {@linkcode ArrayBuffer}. - * @param data The {@linkcode RawPixelData} object carrying the **raw** RGB image data and describing it's format. - * @param allowGpu If `allowGpu` is set to `true` and the given {@linkcode data} is a GPU-buffer, the {@linkcode Image} - * might be wrapping the given GPU-buffer without performing a copy. By default, `allowGpu` is `false` - * @throws If the given {@linkcode RawPixelData} is not a valid RGB buffer representing an {@linkcode Image}. - * @note The given pixel data has to have pre-multiplied alpha, and be some kind of RGB format with 4-bytes-per-pixel. - */ - loadFromRawPixelDataAsync( - data: RawPixelData, - allowGpu?: boolean, - ): Promise; + /** + * Synchronously loads an {@linkcode Image} from the given {@linkcode RawPixelData}'s {@linkcode ArrayBuffer}. + * @param data The {@linkcode RawPixelData} object carrying the **raw** RGB image data and describing it's format. + * @param allowGpu If `allowGpu` is set to `true` and the given {@linkcode data} is a GPU-buffer, the {@linkcode Image} + * might be wrapping the given GPU-buffer without performing a copy. By default, `allowGpu` is `false` + * @throws If the given {@linkcode RawPixelData} is not a valid RGB buffer representing an {@linkcode Image}. + * @note The given pixel data has to have pre-multiplied alpha, and be some kind of RGB format with 4-bytes-per-pixel. + */ + loadFromRawPixelData(data: RawPixelData, allowGpu?: boolean): Image + /** + * Asynchronously loads an {@linkcode Image} from the given {@linkcode RawPixelData}'s {@linkcode ArrayBuffer}. + * @param data The {@linkcode RawPixelData} object carrying the **raw** RGB image data and describing it's format. + * @param allowGpu If `allowGpu` is set to `true` and the given {@linkcode data} is a GPU-buffer, the {@linkcode Image} + * might be wrapping the given GPU-buffer without performing a copy. By default, `allowGpu` is `false` + * @throws If the given {@linkcode RawPixelData} is not a valid RGB buffer representing an {@linkcode Image}. + * @note The given pixel data has to have pre-multiplied alpha, and be some kind of RGB format with 4-bytes-per-pixel. + */ + loadFromRawPixelDataAsync( + data: RawPixelData, + allowGpu?: boolean, + ): Promise - /** - * Synchronously loads an {@linkcode Image} from the given {@linkcode EncodedImageData}'s {@linkcode ArrayBuffer}. - * @param buffer The ArrayBuffer carrying the encoded Image data in any supported image format (JPG, PNG, ...) - * @throws If the given {@linkcode EncodedImageData} is not a valid representation of an {@linkcode Image}. - */ - loadFromEncodedImageData(data: EncodedImageData): Image; - /** - * Asynchronously loads an {@linkcode Image} from the given {@linkcode EncodedImageData}'s {@linkcode ArrayBuffer}. - * @param buffer The ArrayBuffer carrying the encoded Image data in any supported image format (JPG, PNG, ...) - * @throws If the given {@linkcode EncodedImageData} is not a valid representation of an {@linkcode Image}. - */ - loadFromEncodedImageDataAsync(data: EncodedImageData): Promise; + /** + * Synchronously loads an {@linkcode Image} from the given {@linkcode EncodedImageData}'s {@linkcode ArrayBuffer}. + * @param buffer The ArrayBuffer carrying the encoded Image data in any supported image format (JPG, PNG, ...) + * @throws If the given {@linkcode EncodedImageData} is not a valid representation of an {@linkcode Image}. + */ + loadFromEncodedImageData(data: EncodedImageData): Image + /** + * Asynchronously loads an {@linkcode Image} from the given {@linkcode EncodedImageData}'s {@linkcode ArrayBuffer}. + * @param buffer The ArrayBuffer carrying the encoded Image data in any supported image format (JPG, PNG, ...) + * @throws If the given {@linkcode EncodedImageData} is not a valid representation of an {@linkcode Image}. + */ + loadFromEncodedImageDataAsync(data: EncodedImageData): Promise - /** - * Synchronously decodes the given {@linkcode thumbhash} (and {@linkcode ArrayBuffer}) - * into an {@linkcode Image}. - * @param buffer The ArrayBuffer carrying the ThumbHash's data - * @throws If the given {@linkcode thumbhash} is not a valid ThumbHash. - */ - loadFromThumbHash(thumbhash: ArrayBuffer): Image; - loadFromThumbHashAsync(thumbhash: ArrayBuffer): Promise; + /** + * Synchronously decodes the given {@linkcode thumbhash} (and {@linkcode ArrayBuffer}) + * into an {@linkcode Image}. + * @param buffer The ArrayBuffer carrying the ThumbHash's data + * @throws If the given {@linkcode thumbhash} is not a valid ThumbHash. + */ + loadFromThumbHash(thumbhash: ArrayBuffer): Image + loadFromThumbHashAsync(thumbhash: ArrayBuffer): Promise } diff --git a/packages/react-native-nitro-image/src/specs/ImageLoader.nitro.ts b/packages/react-native-nitro-image/src/specs/ImageLoader.nitro.ts index 4d7df8c2..de014dc6 100644 --- a/packages/react-native-nitro-image/src/specs/ImageLoader.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/ImageLoader.nitro.ts @@ -1,24 +1,24 @@ -import type { HybridObject } from "react-native-nitro-modules"; -import type { Image } from "./Image.nitro"; -import type { NitroImageView } from "./ImageView.nitro"; +import type { HybridObject } from 'react-native-nitro-modules' +import type { Image } from './Image.nitro' +import type { NitroImageView } from './ImageView.nitro' export interface ImageLoader - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - /** - * Imperatively loads this `Image` using the underlying load implementation. - */ - loadImage(): Promise; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Imperatively loads this `Image` using the underlying load implementation. + */ + loadImage(): Promise - /** - * Called by an Image View when it becomes visible. - * The native implementation must set the `image` on the `imageView` by downcasting it to a concrete type. - * @param forView The native view type (e.g. `HybridImageView`) - */ - requestImage(forView: NitroImageView): void; - /** - * Called by an Image View when it becomes invisible. - * The native implementation can remove the `image` on the `imageView` if needed to save memory. - * @param forView The native view type (e.g. `HybridImageView`) - */ - dropImage(forView: NitroImageView): void; + /** + * Called by an Image View when it becomes visible. + * The native implementation must set the `image` on the `imageView` by downcasting it to a concrete type. + * @param forView The native view type (e.g. `HybridImageView`) + */ + requestImage(forView: NitroImageView): void + /** + * Called by an Image View when it becomes invisible. + * The native implementation can remove the `image` on the `imageView` if needed to save memory. + * @param forView The native view type (e.g. `HybridImageView`) + */ + dropImage(forView: NitroImageView): void } diff --git a/packages/react-native-nitro-image/src/specs/ImageLoaderFactory.nitro.ts b/packages/react-native-nitro-image/src/specs/ImageLoaderFactory.nitro.ts index eea59050..c54af027 100644 --- a/packages/react-native-nitro-image/src/specs/ImageLoaderFactory.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/ImageLoaderFactory.nitro.ts @@ -1,12 +1,12 @@ -import type { HybridObject } from "react-native-nitro-modules"; -import type { EncodedImageData, RawPixelData } from "./Image.nitro"; -import type { ImageLoader } from "./ImageLoader.nitro"; +import type { HybridObject } from 'react-native-nitro-modules' +import type { EncodedImageData, RawPixelData } from './Image.nitro' +import type { ImageLoader } from './ImageLoader.nitro' export interface ImageLoaderFactory - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - createFileImageLoader(filePath: string): ImageLoader; - createResourceImageLoader(name: string): ImageLoader; - createSymbolImageLoader(symbolName: string): ImageLoader; - createRawPixelDataImageLoader(data: RawPixelData): ImageLoader; - createEncodedImageDataImageLoader(data: EncodedImageData): ImageLoader; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + createFileImageLoader(filePath: string): ImageLoader + createResourceImageLoader(name: string): ImageLoader + createSymbolImageLoader(symbolName: string): ImageLoader + createRawPixelDataImageLoader(data: RawPixelData): ImageLoader + createEncodedImageDataImageLoader(data: EncodedImageData): ImageLoader } diff --git a/packages/react-native-nitro-image/src/specs/ImageUtils.nitro.ts b/packages/react-native-nitro-image/src/specs/ImageUtils.nitro.ts index cea18c0f..186aaeab 100644 --- a/packages/react-native-nitro-image/src/specs/ImageUtils.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/ImageUtils.nitro.ts @@ -1,24 +1,24 @@ -import type { HybridObject } from "react-native-nitro-modules"; +import type { HybridObject } from 'react-native-nitro-modules' export interface ImageUtils - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - /** - * Returns `true` when the host platform supports loading Images - * in `HEIC` format. - */ - readonly supportsHeicLoading: boolean; - /** - * Returns `true` when the host platform supports writing Images - * in `HEIC` format. - */ - readonly supportsHeicWriting: boolean; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Returns `true` when the host platform supports loading Images + * in `HEIC` format. + */ + readonly supportsHeicLoading: boolean + /** + * Returns `true` when the host platform supports writing Images + * in `HEIC` format. + */ + readonly supportsHeicWriting: boolean - /** - * Converts the given ThumbHash {@linkcode ArrayBuffer} to a `string`. - */ - thumbHashToBase64String(thumbhash: ArrayBuffer): string; - /** - * Converts the given ThumbHash `string` to an {@linkcode ArrayBuffer}. - */ - thumbhashFromBase64String(thumbhashBase64: string): ArrayBuffer; + /** + * Converts the given ThumbHash {@linkcode ArrayBuffer} to a `string`. + */ + thumbHashToBase64String(thumbhash: ArrayBuffer): string + /** + * Converts the given ThumbHash `string` to an {@linkcode ArrayBuffer}. + */ + thumbhashFromBase64String(thumbhashBase64: string): ArrayBuffer } diff --git a/packages/react-native-nitro-image/src/specs/ImageView.nitro.ts b/packages/react-native-nitro-image/src/specs/ImageView.nitro.ts index a7e0bb47..572a3a69 100644 --- a/packages/react-native-nitro-image/src/specs/ImageView.nitro.ts +++ b/packages/react-native-nitro-image/src/specs/ImageView.nitro.ts @@ -1,10 +1,10 @@ import type { - HybridView, - HybridViewMethods, - HybridViewProps, -} from "react-native-nitro-modules"; -import type { Image } from "./Image.nitro"; -import type { ImageLoader } from "./ImageLoader.nitro"; + HybridView, + HybridViewMethods, + HybridViewProps, +} from 'react-native-nitro-modules' +import type { Image } from './Image.nitro' +import type { ImageLoader } from './ImageLoader.nitro' /** * A resizing mode for fitting an Image inside an Image View. @@ -13,55 +13,55 @@ import type { ImageLoader } from "./ImageLoader.nitro"; * - `center`: Center the content in the view’s bounds, keeping the proportions the same. * - `stretch`: Scale the content to fit the size of itself by changing the aspect ratio of the content if necessary. */ -export type ResizeMode = "cover" | "contain" | "center" | "stretch"; +export type ResizeMode = 'cover' | 'contain' | 'center' | 'stretch' export interface NativeNitroImageViewProps extends HybridViewProps { - /** - * Represents the image actually shown in this Image View. - * - {@linkcode Image}: Shows a specific in-memory {@linkcode Image} - * instance. Even when the view goes invisible, the image will still - * be in-memory. - * - {@linkcode ImageLoader}: Asynchronously loads an image from the - * given {@linkcode ImageLoader} into the view when it becomes visible - * ({@linkcode ImageLoader.requestImage | requestImage(…)}), - * and drops it again when the view becomes invisible - * ({@linkcode ImageLoader.dropImage | dropImage(…)}). This is - * more efficient and works better for Lists. - * - `undefined`: Shows no image. - * @default undefined - */ - image?: Image | ImageLoader; - /** - * Specifies the resizing mode that will be applied when the Image - * does not exactly match the Image View's width and height. - * @see {@linkcode ResizeMode} - * @default 'cover' - */ - resizeMode?: ResizeMode; - /** - * A key that uniquely identifies an Image that should be displayed. - * - * If the {@linkcode recyclingKey} changes, the displayed {@linkcode Image} - * will be cleared to display _nothing_, until the next {@linkcode Image} - * has been loaded. - * - * It is recommended to set this to the {@linkcode Image}'s URL in - * large lists to prevent the recycled views from displaying - * stale {@linkcode Image} instances. - * @default undefined - * @example - * ```tsx - * - * ``` - */ - recyclingKey?: string; + /** + * Represents the image actually shown in this Image View. + * - {@linkcode Image}: Shows a specific in-memory {@linkcode Image} + * instance. Even when the view goes invisible, the image will still + * be in-memory. + * - {@linkcode ImageLoader}: Asynchronously loads an image from the + * given {@linkcode ImageLoader} into the view when it becomes visible + * ({@linkcode ImageLoader.requestImage | requestImage(…)}), + * and drops it again when the view becomes invisible + * ({@linkcode ImageLoader.dropImage | dropImage(…)}). This is + * more efficient and works better for Lists. + * - `undefined`: Shows no image. + * @default undefined + */ + image?: Image | ImageLoader + /** + * Specifies the resizing mode that will be applied when the Image + * does not exactly match the Image View's width and height. + * @see {@linkcode ResizeMode} + * @default 'cover' + */ + resizeMode?: ResizeMode + /** + * A key that uniquely identifies an Image that should be displayed. + * + * If the {@linkcode recyclingKey} changes, the displayed {@linkcode Image} + * will be cleared to display _nothing_, until the next {@linkcode Image} + * has been loaded. + * + * It is recommended to set this to the {@linkcode Image}'s URL in + * large lists to prevent the recycled views from displaying + * stale {@linkcode Image} instances. + * @default undefined + * @example + * ```tsx + * + * ``` + */ + recyclingKey?: string } export interface NativeNitroImageViewMethods extends HybridViewMethods { - // no methods + // no methods } export type NitroImageView = HybridView< - NativeNitroImageViewProps, - NativeNitroImageViewMethods ->; + NativeNitroImageViewProps, + NativeNitroImageViewMethods +> diff --git a/packages/react-native-nitro-image/src/useImage.ts b/packages/react-native-nitro-image/src/useImage.ts index 0030397a..e1f3b6b0 100644 --- a/packages/react-native-nitro-image/src/useImage.ts +++ b/packages/react-native-nitro-image/src/useImage.ts @@ -1,25 +1,25 @@ -import { useEffect, useState } from "react"; -import { type AsyncImageSource, isHybridObject } from "./AsyncImageSource"; -import { loadImage } from "./loadImage"; -import { markHybridObject } from "./markHybridObject"; -import type { Image } from "./specs/Image.nitro"; +import { useEffect, useState } from 'react' +import { type AsyncImageSource, isHybridObject } from './AsyncImageSource' +import { loadImage } from './loadImage' +import { markHybridObject } from './markHybridObject' +import type { Image } from './specs/Image.nitro' type Result = - // Loading State - | { - image: undefined; - error: undefined; - } - // Loaded state - | { - image: Image; - error: undefined; - } - // Error state - | { - image: undefined; - error: Error; - }; + // Loading State + | { + image: undefined + error: undefined + } + // Loaded state + | { + image: Image + error: undefined + } + // Error state + | { + image: undefined + error: Error + } /** * A hook to asynchronously load an image from the @@ -30,27 +30,27 @@ type Result = * ``` */ export function useImage(source: AsyncImageSource): Result { - const [image, setImage] = useState({ - image: undefined, - error: undefined, - }); + const [image, setImage] = useState({ + image: undefined, + error: undefined, + }) - // biome-ignore lint: The dependencies array is a bit hacky. - useEffect(() => { - (async () => { - try { - // 1. Create the Image/ImageLoader instance - const result = await loadImage(source); - // 2. Add `__source` as a property on the JS side so React diffs properly - markHybridObject(result, source); - // 3. Update the state - setImage({ image: result, error: undefined }); - } catch (e) { - const error = e instanceof Error ? e : new Error(`${e}`); - setImage({ image: undefined, error: error }); - } - })(); - }, [isHybridObject(source) ? source : JSON.stringify(source)]); + // biome-ignore lint: The dependencies array is a bit hacky. + useEffect(() => { + ;(async () => { + try { + // 1. Create the Image/ImageLoader instance + const result = await loadImage(source) + // 2. Add `__source` as a property on the JS side so React diffs properly + markHybridObject(result, source) + // 3. Update the state + setImage({ image: result, error: undefined }) + } catch (e) { + const error = e instanceof Error ? e : new Error(`${e}`) + setImage({ image: undefined, error: error }) + } + })() + }, [isHybridObject(source) ? source : JSON.stringify(source)]) - return image; + return image } diff --git a/packages/react-native-nitro-image/src/useImageLoader.ts b/packages/react-native-nitro-image/src/useImageLoader.ts index dda4e71d..dc3d5b67 100644 --- a/packages/react-native-nitro-image/src/useImageLoader.ts +++ b/packages/react-native-nitro-image/src/useImageLoader.ts @@ -1,20 +1,20 @@ -import { useMemo } from "react"; -import { type AsyncImageSource, isHybridObject } from "./AsyncImageSource"; -import { createImageLoader } from "./createImageLoader"; -import { markHybridObject } from "./markHybridObject"; -import type { Image } from "./specs/Image.nitro"; -import type { ImageLoader } from "./specs/ImageLoader.nitro"; +import { useMemo } from 'react' +import { type AsyncImageSource, isHybridObject } from './AsyncImageSource' +import { createImageLoader } from './createImageLoader' +import { markHybridObject } from './markHybridObject' +import type { Image } from './specs/Image.nitro' +import type { ImageLoader } from './specs/ImageLoader.nitro' export function useImageLoader( - source: AsyncImageSource, + source: AsyncImageSource, ): Image | ImageLoader | undefined { - // biome-ignore lint: The dependencies array is a bit hacky. - return useMemo(() => { - // 1. Create the Image/ImageLoader instance - const loader = createImageLoader(source); - // 2. Add `__source` as a property on the JS side so React diffs properly - markHybridObject(loader, source); - // 3. Return it - return loader; - }, [isHybridObject(source) ? source : JSON.stringify(source)]); + // biome-ignore lint: The dependencies array is a bit hacky. + return useMemo(() => { + // 1. Create the Image/ImageLoader instance + const loader = createImageLoader(source) + // 2. Add `__source` as a property on the JS side so React diffs properly + markHybridObject(loader, source) + // 3. Return it + return loader + }, [isHybridObject(source) ? source : JSON.stringify(source)]) } diff --git a/packages/react-native-nitro-image/tsconfig.build.json b/packages/react-native-nitro-image/tsconfig.build.json index 242f054a..010c9367 100644 --- a/packages/react-native-nitro-image/tsconfig.build.json +++ b/packages/react-native-nitro-image/tsconfig.build.json @@ -1,11 +1,11 @@ { - "extends": [ - "@tsconfig/react-native/tsconfig.json", - "../../config/tsconfig.json" - ], - "include": ["src"], - "compilerOptions": { - "rootDir": "src", - "jsx": "react" - } + "extends": [ + "@tsconfig/react-native/tsconfig.json", + "../../config/tsconfig.json" + ], + "include": ["src"], + "compilerOptions": { + "rootDir": "src", + "jsx": "react" + } } diff --git a/packages/react-native-nitro-image/tsconfig.json b/packages/react-native-nitro-image/tsconfig.json index 7e22651f..bea65346 100644 --- a/packages/react-native-nitro-image/tsconfig.json +++ b/packages/react-native-nitro-image/tsconfig.json @@ -1,12 +1,12 @@ { - "extends": [ - "@tsconfig/react-native/tsconfig.json", - "../../config/tsconfig.json" - ], - "include": ["src"], - "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "jsx": "react" - } + "extends": [ + "@tsconfig/react-native/tsconfig.json", + "../../config/tsconfig.json" + ], + "include": ["src"], + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "jsx": "react" + } } diff --git a/packages/react-native-nitro-web-image/babel.config.js b/packages/react-native-nitro-web-image/babel.config.js index f8228843..3e0218e6 100644 --- a/packages/react-native-nitro-web-image/babel.config.js +++ b/packages/react-native-nitro-web-image/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: ["module:@react-native/babel-preset"], -}; + presets: ['module:@react-native/babel-preset'], +} diff --git a/packages/react-native-nitro-web-image/nitro.json b/packages/react-native-nitro-web-image/nitro.json index e7b7563b..a3fcc25c 100644 --- a/packages/react-native-nitro-web-image/nitro.json +++ b/packages/react-native-nitro-web-image/nitro.json @@ -1,18 +1,18 @@ { - "$schema": "https://nitro.margelo.com/nitro.schema.json", - "cxxNamespace": ["web", "image"], - "ios": { - "iosModuleName": "NitroWebImage" - }, - "android": { - "androidNamespace": ["web", "image"], - "androidCxxLibName": "NitroWebImage" - }, - "autolinking": { - "WebImageFactory": { - "swift": "HybridWebImageFactory", - "kotlin": "HybridWebImageFactory" - } - }, - "ignorePaths": ["**/node_modules"] + "$schema": "https://nitro.margelo.com/nitro.schema.json", + "cxxNamespace": ["web", "image"], + "ios": { + "iosModuleName": "NitroWebImage" + }, + "android": { + "androidNamespace": ["web", "image"], + "androidCxxLibName": "NitroWebImage" + }, + "autolinking": { + "WebImageFactory": { + "swift": "HybridWebImageFactory", + "kotlin": "HybridWebImageFactory" + } + }, + "ignorePaths": ["**/node_modules"] } diff --git a/packages/react-native-nitro-web-image/package.json b/packages/react-native-nitro-web-image/package.json index 666ff450..03efb621 100644 --- a/packages/react-native-nitro-web-image/package.json +++ b/packages/react-native-nitro-web-image/package.json @@ -1,110 +1,99 @@ { - "name": "react-native-nitro-web-image", - "version": "0.10.2", - "description": "A superfast in-memory Image type and view component for React Native Web, built with Nitro!", - "main": "lib/commonjs/index", - "module": "lib/module/index", - "types": "lib/typescript/index.d.ts", - "react-native": "src/index", - "source": "src/index", - "workspaces": [ - "example" - ], - "files": [ - "src", - "react-native.config.js", - "lib", - "nitrogen", - "android/build.gradle", - "android/gradle.properties", - "android/CMakeLists.txt", - "android/src", - "ios/**/*.h", - "ios/**/*.m", - "ios/**/*.mm", - "ios/**/*.cpp", - "ios/**/*.swift", - "app.plugin.js", - "nitro.json", - "*.podspec", - "README.md" - ], - "scripts": { - "build": "rm -rf tsconfig.tsbuildinfo && rm -rf lib && bun typecheck && bob build", - "typecheck": "tsc --noEmit", - "clean": "rm -rf android/build node_modules/**/android/build lib", - "lint": "biome check . --fix", - "lint-ci": "biome check .", - "release": "release-it", - "typescript": "tsc", - "specs": "tsc && nitrogen --logLevel=\"debug\"" - }, - "keywords": [ - "react-native", - "nitro" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/mrousavy/react-native-nitro-image.git", - "directory": "packages/react-native-nitro-web-image" - }, - "author": "Marc Rousavy (https://github.com/mrousavy)", - "license": "MIT", - "bugs": { - "url": "https://github.com/mrousavy/react-native-nitro-image/issues" - }, - "homepage": "https://github.com/mrousavy/react-native-nitro-image#readme", - "publishConfig": { - "registry": "https://registry.npmjs.org/" - }, - "devDependencies": { - "@biomejs/biome": "2.2.6", - "@types/react": "^19.0.6", - "nitrogen": "0.33.4", - "react": "19.1.0", - "react-native": "0.81.0", - "react-native-nitro-modules": "0.33.4", - "typescript": "5.8.3" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-nitro-modules": "*", - "react-native-nitro-image": "*" - }, - "prettier": { - "quoteProps": "consistent", - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "es5", - "useTabs": false, - "semi": false + "name": "react-native-nitro-web-image", + "version": "0.10.2", + "description": "A superfast in-memory Image type and view component for React Native Web, built with Nitro!", + "main": "lib/commonjs/index", + "module": "lib/module/index", + "types": "lib/typescript/index.d.ts", + "react-native": "src/index", + "source": "src/index", + "workspaces": [ + "example" + ], + "files": [ + "src", + "react-native.config.js", + "lib", + "nitrogen", + "android/build.gradle", + "android/gradle.properties", + "android/CMakeLists.txt", + "android/src", + "ios/**/*.h", + "ios/**/*.m", + "ios/**/*.mm", + "ios/**/*.cpp", + "ios/**/*.swift", + "app.plugin.js", + "nitro.json", + "*.podspec", + "README.md" + ], + "scripts": { + "build": "rm -rf tsconfig.tsbuildinfo && rm -rf lib && bun typecheck && bob build", + "typecheck": "tsc --noEmit", + "clean": "rm -rf android/build node_modules/**/android/build lib", + "release": "release-it", + "typescript": "tsc", + "specs": "tsc && nitrogen --logLevel=\"debug\"" + }, + "keywords": [ + "react-native", + "nitro" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/mrousavy/react-native-nitro-image.git", + "directory": "packages/react-native-nitro-web-image" + }, + "author": "Marc Rousavy (https://github.com/mrousavy)", + "license": "MIT", + "bugs": { + "url": "https://github.com/mrousavy/react-native-nitro-image/issues" + }, + "homepage": "https://github.com/mrousavy/react-native-nitro-image#readme", + "publishConfig": { + "registry": "https://registry.npmjs.org/" + }, + "devDependencies": { + "@types/react": "^19.0.6", + "nitrogen": "0.33.4", + "react": "19.1.0", + "react-native": "0.81.0", + "react-native-nitro-modules": "0.33.4", + "typescript": "5.8.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-nitro-modules": "*", + "react-native-nitro-image": "*" + }, + "react-native-builder-bob": { + "source": "src", + "output": "lib", + "targets": [ + "commonjs", + "module", + [ + "typescript", + { + "project": "tsconfig.build.json" + } + ] + ] + }, + "release-it": { + "npm": { + "publish": true }, - "react-native-builder-bob": { - "source": "src", - "output": "lib", - "targets": [ - "commonjs", - "module", - [ - "typescript", - { - "project": "tsconfig.build.json" - } - ] - ] + "git": false, + "github": { + "release": false }, - "release-it": { - "npm": { - "publish": true - }, - "git": false, - "github": { - "release": false - }, - "hooks": { - "before:init": "bun typecheck && bun lint", - "after:bump": "bun run build" - } + "hooks": { + "before:init": "bun typecheck && bun lint", + "after:bump": "bun run build" } + } } diff --git a/packages/react-native-nitro-web-image/react-native.config.js b/packages/react-native-nitro-web-image/react-native.config.js index 13a61e95..3fdf8eaa 100644 --- a/packages/react-native-nitro-web-image/react-native.config.js +++ b/packages/react-native-nitro-web-image/react-native.config.js @@ -1,16 +1,16 @@ // https://github.com/react-native-community/cli/blob/main/docs/dependencies.md module.exports = { - dependency: { - platforms: { - /** - * @type {import('@react-native-community/cli-types').IOSDependencyParams} - */ - ios: {}, - /** - * @type {import('@react-native-community/cli-types').AndroidDependencyParams} - */ - android: {}, - }, + dependency: { + platforms: { + /** + * @type {import('@react-native-community/cli-types').IOSDependencyParams} + */ + ios: {}, + /** + * @type {import('@react-native-community/cli-types').AndroidDependencyParams} + */ + android: {}, }, -}; + }, +} diff --git a/packages/react-native-nitro-web-image/src/index.ts b/packages/react-native-nitro-web-image/src/index.ts index 71abf07b..a50e4695 100644 --- a/packages/react-native-nitro-web-image/src/index.ts +++ b/packages/react-native-nitro-web-image/src/index.ts @@ -1,5 +1,5 @@ -import { NitroModules } from "react-native-nitro-modules"; -import type { WebImageFactory } from "./specs/WebImageFactory.nitro"; +import { NitroModules } from 'react-native-nitro-modules' +import type { WebImageFactory } from './specs/WebImageFactory.nitro' export const WebImages = - NitroModules.createHybridObject("WebImageFactory"); + NitroModules.createHybridObject('WebImageFactory') diff --git a/packages/react-native-nitro-web-image/src/specs/WebImageFactory.nitro.ts b/packages/react-native-nitro-web-image/src/specs/WebImageFactory.nitro.ts index b2104f21..4254b012 100644 --- a/packages/react-native-nitro-web-image/src/specs/WebImageFactory.nitro.ts +++ b/packages/react-native-nitro-web-image/src/specs/WebImageFactory.nitro.ts @@ -1,103 +1,100 @@ -import type { Image, ImageLoader } from "react-native-nitro-image"; -import type { HybridObject } from "react-native-nitro-modules"; +import type { Image, ImageLoader } from 'react-native-nitro-image' +import type { HybridObject } from 'react-native-nitro-modules' -export type AsyncImagePriority = "low" | "default" | "high"; +export type AsyncImagePriority = 'low' | 'default' | 'high' export interface AsyncImageLoadOptions { - /** - * Specifies the priority of the image download. - * @default 'default' - */ - priority?: AsyncImagePriority; + /** + * Specifies the priority of the image download. + * @default 'default' + */ + priority?: AsyncImagePriority - /** - * Forces a cache refresh even if the URL is changed. - * Use this if you cannot make your URLs static. - * @default false - */ - forceRefresh?: boolean; + /** + * Forces a cache refresh even if the URL is changed. + * Use this if you cannot make your URLs static. + * @default false + */ + forceRefresh?: boolean - /** - * A custom cache key to use for the image. By default, the URL is used as a cache key. - * For customized access control/caching, provide a custom cache key. - * @default The URL of the image. - */ - cacheKey?: string; + /** + * A custom cache key to use for the image. By default, the URL is used as a cache key. + * For customized access control/caching, provide a custom cache key. + * @default The URL of the image. + */ + cacheKey?: string - /** - * Allows the Image download to continue even when the app is backgrounded. - * @default false - */ - continueInBackground?: boolean; + /** + * Allows the Image download to continue even when the app is backgrounded. + * @default false + */ + continueInBackground?: boolean - /** - * Enable to allow untrusted SSL certificates. - * @default false - */ - allowInvalidSSLCertificates?: boolean; + /** + * Enable to allow untrusted SSL certificates. + * @default false + */ + allowInvalidSSLCertificates?: boolean - /** - * Scales down larger images to respect the device's memory constraints (max. 60 MB, or 4096x4096) - * @default false - */ - scaleDownLargeImages?: boolean; + /** + * Scales down larger images to respect the device's memory constraints (max. 60 MB, or 4096x4096) + * @default false + */ + scaleDownLargeImages?: boolean - /** - * By default, cached images are queried from memory **asynchronously** to avoid UI lag. - * If this flag is enabled, images are queried **synchronously** from memory. - * @default false - */ - queryMemoryDataSync?: boolean; - /** - * By default, cached images are queried from disk **asynchronously** to avoid UI lag. - * If this flag is enabled, images are queried **synchronously** from disk. - * @default false - */ - queryDiskDataSync?: boolean; + /** + * By default, cached images are queried from memory **asynchronously** to avoid UI lag. + * If this flag is enabled, images are queried **synchronously** from memory. + * @default false + */ + queryMemoryDataSync?: boolean + /** + * By default, cached images are queried from disk **asynchronously** to avoid UI lag. + * If this flag is enabled, images are queried **synchronously** from disk. + * @default false + */ + queryDiskDataSync?: boolean - /** - * By default, images are decoded from binary data to actual image representations. - * Disabling this might speed up downloads, but could increase memory usage. - * @default true - */ - decodeImage?: boolean; + /** + * By default, images are decoded from binary data to actual image representations. + * Disabling this might speed up downloads, but could increase memory usage. + * @default true + */ + decodeImage?: boolean - /** - * By default, images can be rendered using hardware textures, - * which is more efficient compared to a software implementation. - * - * If you run into issues with the hardware renderer, disable `allowHardware`. - * @platform Android - * @default true - */ - allowHardware?: boolean; + /** + * By default, images can be rendered using hardware textures, + * which is more efficient compared to a software implementation. + * + * If you run into issues with the hardware renderer, disable `allowHardware`. + * @platform Android + * @default true + */ + allowHardware?: boolean } export interface WebImageFactory - extends HybridObject<{ ios: "swift"; android: "kotlin" }> { - /** - * Create a deferred {@linkcode ImageLoader} that loads the {@linkcode Image} - * from the given {@linkcode url}. - */ - createWebImageLoader( - url: string, - options?: AsyncImageLoadOptions, - ): ImageLoader; - /** - * Fetches data from the given {@linkcode url} and decode it into an {@linkcode Image}. - * This is async and can throw. - */ - loadFromURLAsync( - url: string, - options?: AsyncImageLoadOptions, - ): Promise; + extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> { + /** + * Create a deferred {@linkcode ImageLoader} that loads the {@linkcode Image} + * from the given {@linkcode url}. + */ + createWebImageLoader( + url: string, + options?: AsyncImageLoadOptions, + ): ImageLoader + /** + * Fetches data from the given {@linkcode url} and decode it into an {@linkcode Image}. + * This is async and can throw. + */ + loadFromURLAsync(url: string, options?: AsyncImageLoadOptions): Promise - /** - * Pre-loads the {@linkcode Image} at the given {@linkcode url} - * and store it in cache. - * - * If the preload succeeds, the next call to - * {@linkcode loadFromURLAsync} will be a cache hit. - */ - preload(url: string): void; + /** + * Pre-loads the {@linkcode Image} at the given {@linkcode url} + * and store it in cache. + * + * If the preload succeeds, the next call to + * {@linkcode loadFromURLAsync} will be a cache hit. + */ + preload(url: string): void } diff --git a/packages/react-native-nitro-web-image/tsconfig.build.json b/packages/react-native-nitro-web-image/tsconfig.build.json index 242f054a..010c9367 100644 --- a/packages/react-native-nitro-web-image/tsconfig.build.json +++ b/packages/react-native-nitro-web-image/tsconfig.build.json @@ -1,11 +1,11 @@ { - "extends": [ - "@tsconfig/react-native/tsconfig.json", - "../../config/tsconfig.json" - ], - "include": ["src"], - "compilerOptions": { - "rootDir": "src", - "jsx": "react" - } + "extends": [ + "@tsconfig/react-native/tsconfig.json", + "../../config/tsconfig.json" + ], + "include": ["src"], + "compilerOptions": { + "rootDir": "src", + "jsx": "react" + } } diff --git a/packages/react-native-nitro-web-image/tsconfig.json b/packages/react-native-nitro-web-image/tsconfig.json index 7e22651f..bea65346 100644 --- a/packages/react-native-nitro-web-image/tsconfig.json +++ b/packages/react-native-nitro-web-image/tsconfig.json @@ -1,12 +1,12 @@ { - "extends": [ - "@tsconfig/react-native/tsconfig.json", - "../../config/tsconfig.json" - ], - "include": ["src"], - "compilerOptions": { - "rootDir": "src", - "outDir": "lib", - "jsx": "react" - } + "extends": [ + "@tsconfig/react-native/tsconfig.json", + "../../config/tsconfig.json" + ], + "include": ["src"], + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "jsx": "react" + } }