diff --git a/packages/assets-registry/README.md b/packages/assets-registry/README.md index 791fdc592aa9..b61cd3a7a8d3 100644 --- a/packages/assets-registry/README.md +++ b/packages/assets-registry/README.md @@ -11,7 +11,14 @@ Most apps never import this directly — assets are handled through ``. ## API -### `@react-native/assets-registry/registry` +### `@react-native/assets-registry/registry` (DEPRECATED) + +> [!Warning] +> **Deprecated**: Aliases to [`AssetRegistry`](https://reactnative.dev/docs/assetregistry) (since 0.87). +> +> Please use: +> - `import { AssetRegistry } from 'react-native';` (apps/library code) +> - `'react-native/asset-registry'` (entrypoint for Metro/build configs) | Export | Signature | Notes | |---|---|---| diff --git a/packages/assets-registry/package.json b/packages/assets-registry/package.json index f3c3c9664f06..645604cc9846 100644 --- a/packages/assets-registry/package.json +++ b/packages/assets-registry/package.json @@ -26,5 +26,8 @@ "!**/__fixtures__/**", "!**/__mocks__/**", "!**/__tests__/**" - ] + ], + "peerDependencies": { + "react-native": "*" + } } diff --git a/packages/assets-registry/path-support.js b/packages/assets-registry/path-support.js index c73a2e2bbf2e..f7e51a9e6ecd 100644 --- a/packages/assets-registry/path-support.js +++ b/packages/assets-registry/path-support.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict + * @flow strict-local * @format */ diff --git a/packages/assets-registry/registry.d.ts b/packages/assets-registry/registry.d.ts index 7509ef9ba13e..fb8bfcc2a156 100644 --- a/packages/assets-registry/registry.d.ts +++ b/packages/assets-registry/registry.d.ts @@ -7,8 +7,14 @@ * @format */ +/** + * @deprecated Use `import type {AssetDestPathResolver} from 'react-native'` instead. + */ export type AssetDestPathResolver = 'android' | 'generic'; +/** + * @deprecated Use `import type {PackagerAsset} from 'react-native'` instead. + */ export type PackagerAsset = { readonly __packager_asset: boolean; readonly fileSystemLocation: string; @@ -22,6 +28,12 @@ export type PackagerAsset = { readonly resolver?: AssetDestPathResolver | undefined; }; +/** + * @deprecated Use `import {AssetRegistry} from 'react-native'` instead. + */ export function registerAsset(asset: PackagerAsset): number; +/** + * @deprecated Use `import {AssetRegistry} from 'react-native'` instead. + */ export function getAssetByID(assetId: number): PackagerAsset; diff --git a/packages/assets-registry/registry.js b/packages/assets-registry/registry.js index d193d3af1c87..08a0ef59fb35 100644 --- a/packages/assets-registry/registry.js +++ b/packages/assets-registry/registry.js @@ -4,41 +4,20 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict + * @flow strict-local * @format */ 'use strict'; -/*:: -export type AssetDestPathResolver = 'android' | 'generic'; +import {AssetRegistry} from 'react-native'; -export type PackagerAsset = { - readonly __packager_asset: boolean, - readonly fileSystemLocation: string, - readonly httpServerLocation: string, - readonly width: ?number, - readonly height: ?number, - readonly scales: Array, - readonly hash: string, - readonly name: string, - readonly type: string, - readonly resolver?: AssetDestPathResolver, - ... -}; +/*:: +export type {AssetDestPathResolver, PackagerAsset} from 'react-native'; */ -const assets /*: Array */ = []; - -function registerAsset(asset /*: PackagerAsset */) /*: number */ { - // `push` returns new array length, so the first asset will - // get id 1 (not 0) to make the value truthy - return assets.push(asset); -} - -function getAssetByID(assetId /*: number */) /*: PackagerAsset */ { - return assets[assetId - 1]; -} - // eslint-disable-next-line @react-native/monorepo/no-commonjs-exports -module.exports = {registerAsset, getAssetByID}; +module.exports = { + registerAsset: AssetRegistry.registerAsset, + getAssetByID: AssetRegistry.getAssetByID, +}; diff --git a/packages/eslint-plugin-react-native/no-deep-imports.js b/packages/eslint-plugin-react-native/no-deep-imports.js index 697c89b9d616..6446c70ba6ac 100644 --- a/packages/eslint-plugin-react-native/no-deep-imports.js +++ b/packages/eslint-plugin-react-native/no-deep-imports.js @@ -32,6 +32,7 @@ module.exports = { if ( !isDeepReactNativeImport(node.source) || isInitializeCoreImport(node.source) || + isSecondaryEntryPoint(node.source) || isFbInternalImport(node.source) ) { return; @@ -88,6 +89,7 @@ module.exports = { if ( !isDeepRequire(node) || isInitializeCoreImport(node.arguments[0]) || + isSecondaryEntryPoint(node.arguments[0]) || isFbInternalImport(node.arguments[0]) ) { return; @@ -173,6 +175,14 @@ module.exports = { return source.value === 'react-native/Libraries/Core/InitializeCore'; } + function isSecondaryEntryPoint(source) { + if (source.type !== 'Literal' || typeof source.value !== 'string') { + return false; + } + + return source.value === 'react-native/asset-registry'; + } + function isFbInternalImport(source) { if (source.type !== 'Literal' || typeof source.value !== 'string') { return false; diff --git a/packages/metro-config/src/index.flow.js b/packages/metro-config/src/index.flow.js index 6bba33bd975a..03a9aed5ace4 100644 --- a/packages/metro-config/src/index.flow.js +++ b/packages/metro-config/src/index.flow.js @@ -84,7 +84,7 @@ export function getDefaultConfig(projectRoot: string): ConfigT { }, transformer: { allowOptionalDependencies: true, - assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry', + assetRegistryPath: 'react-native/asset-registry', asyncRequireModulePath: require.resolve( 'metro-runtime/src/modules/asyncRequire', ), diff --git a/packages/react-native/Libraries/Image/AssetRegistry.js b/packages/react-native/Libraries/Image/AssetRegistry.js deleted file mode 100644 index 3e1785346ff3..000000000000 --- a/packages/react-native/Libraries/Image/AssetRegistry.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow strict - * @format - */ - -export { - registerAsset, - getAssetByID, -} from '@react-native/assets-registry/registry'; diff --git a/packages/react-native/Libraries/Image/AssetSourceResolver.js b/packages/react-native/Libraries/Image/AssetSourceResolver.js index 2b1e9647608a..a515cf096f60 100644 --- a/packages/react-native/Libraries/Image/AssetSourceResolver.js +++ b/packages/react-native/Libraries/Image/AssetSourceResolver.js @@ -10,31 +10,10 @@ 'use strict'; -export type ResolvedAssetSource = { - readonly __packager_asset: boolean, - readonly width: ?number, - readonly height: ?number, - readonly uri: string, - readonly scale: number, -}; - -// From @react-native/assets-registry -type AssetDestPathResolver = 'android' | 'generic'; - -// From @react-native/assets-registry -type PackagerAsset = Readonly<{ - __packager_asset: boolean, - fileSystemLocation: string, - httpServerLocation: string, - width: ?number, - height: ?number, - scales: Array, - hash: string, - name: string, - type: string, - resolver?: AssetDestPathResolver, - ... -}>; +import type { + AssetDestPathResolver, + PackagerAsset, +} from '../../src/private/assets/AssetRegistry'; const PixelRatio = require('../Utilities/PixelRatio').default; const Platform = require('../Utilities/Platform').default; @@ -46,6 +25,14 @@ const { } = require('@react-native/assets-registry/path-support'); const invariant = require('invariant'); +export type ResolvedAssetSource = { + readonly __packager_asset: boolean, + readonly width: ?number, + readonly height: ?number, + readonly uri: string, + readonly scale: number, +}; + /** * Returns a path like 'assets/AwesomeModule/icon@2x.png' */ diff --git a/packages/react-native/Libraries/Image/RelativeImageStub.js b/packages/react-native/Libraries/Image/RelativeImageStub.js index 8472f13dc4a9..73d403a4ecbf 100644 --- a/packages/react-native/Libraries/Image/RelativeImageStub.js +++ b/packages/react-native/Libraries/Image/RelativeImageStub.js @@ -13,7 +13,7 @@ // This is a stub for flow to make it understand require('./icon.png') // See metro/src/Bundler/index.js -const AssetRegistry = require('@react-native/assets-registry/registry'); +const {AssetRegistry} = require('../../src/private/assets/AssetRegistry'); const RelativeImageStub = AssetRegistry.registerAsset({ __packager_asset: true, diff --git a/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js b/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js index dfa7d011fa14..ae68d860e004 100644 --- a/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -8,7 +8,7 @@ * @format */ -import type {PackagerAsset} from '../../../../assets-registry/registry'; +import type {PackagerAsset} from '../../../src/private/assets/AssetRegistry'; import type {ResolvedAssetSource} from '../AssetSourceResolver'; describe('resolveAssetSource', () => { @@ -20,7 +20,8 @@ describe('resolveAssetSource', () => { beforeEach(() => { jest.resetModules(); - AssetRegistry = require('@react-native/assets-registry/registry'); + AssetRegistry = + require('../../../src/private/assets/AssetRegistry').AssetRegistry; resolveAssetSource = require('../resolveAssetSource').default; NativeSourceCode = require('../../NativeModules/specs/NativeSourceCode').default; diff --git a/packages/react-native/Libraries/Image/resolveAssetSource.js b/packages/react-native/Libraries/Image/resolveAssetSource.js index e08a6c848868..9da1b5f45a16 100644 --- a/packages/react-native/Libraries/Image/resolveAssetSource.js +++ b/packages/react-native/Libraries/Image/resolveAssetSource.js @@ -16,10 +16,10 @@ import type {ImageSource} from './ImageSource'; import SourceCode from '../NativeModules/specs/NativeSourceCode'; +const {AssetRegistry} = require('../../src/private/assets/AssetRegistry'); const AssetSourceResolver: AssetSourceResolverT = require('./AssetSourceResolver').default; const {pickScale} = require('./AssetUtils'); -const AssetRegistry = require('@react-native/assets-registry/registry'); type CustomSourceTransformer = ( resolver: AssetSourceResolver, diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 699c2fff455a..dce9ba845872 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<42200de8ca10d30541e23b67547d9a13>> + * @generated SignedSource<<51a4ff32766e21cd2638ff58450f31f9>> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -151,6 +151,10 @@ declare const AnimatedScrollView_default: AnimatedComponentType< > declare const AppState: typeof AppState_default declare const AppState_default: AppStateImpl +declare const AssetRegistry: { + getAssetByID(assetId: number): PackagerAsset + registerAsset(asset: PackagerAsset): number +} declare const attachNativeEvent: typeof $$AnimatedImplementation.attachNativeEvent declare const BackHandler: typeof BackHandler_default declare const BackHandler_default: TBackHandler @@ -1638,6 +1642,8 @@ declare interface ArrayLike_2 extends Iterable { [indexer: number]: T readonly length: number } +declare type AssetDestPathResolver = "android" | "generic" +declare type AssetRegistry = typeof AssetRegistry declare type attachNativeEvent = typeof attachNativeEvent declare function attachNativeEventImpl( viewRef: any, @@ -3479,6 +3485,17 @@ declare type OptionalVirtualizedSectionListProps< declare type OrientationChangeEvent = { readonly orientation: "landscape" | "portrait" } +declare type PackagerAsset = { + readonly fileSystemLocation: string + readonly hash: string + readonly height: number | undefined + readonly httpServerLocation: string + readonly name: string + readonly resolver?: AssetDestPathResolver + readonly scales: Array + readonly type: string + readonly width: number | undefined +} declare type PanResponder = typeof PanResponder declare type PanResponderCallbacks = { readonly onMoveShouldSetPanResponder?: ActiveCallback @@ -5927,6 +5944,8 @@ export { AppStateEvent, // 80f034c3 AppStateStatus, // 447e5ef2 Appearance, // 83e9641a + AssetDestPathResolver, // 59047424 + AssetRegistry, // 6070bb45 AutoCapitalize, // c0e857a0 BackHandler, // f139fc69 BackPressEventName, // 4620fb76 @@ -6060,6 +6079,7 @@ export { NativeUIEvent, // 44ac26ac Networking, // bbc5be42 OpaqueColorValue, // 25f3fa5b + PackagerAsset, // d1c88cf4 PanResponder, // f8f71cac PanResponderCallbacks, // 6d63e7be PanResponderGestureState, // 54baf558 diff --git a/packages/react-native/index.js b/packages/react-native/index.js index 69c3f67ca8b1..3b3c566ccd8c 100644 --- a/packages/react-native/index.js +++ b/packages/react-native/index.js @@ -211,6 +211,9 @@ module.exports = { get AppState() { return require('./Libraries/AppState/AppState').default; }, + get AssetRegistry() { + return require('./src/private/assets/AssetRegistry').AssetRegistry; + }, get BackHandler() { return require('./Libraries/Utilities/BackHandler').default; }, diff --git a/packages/react-native/index.js.flow b/packages/react-native/index.js.flow index 53c4818af9ce..9869ed8c5907 100644 --- a/packages/react-native/index.js.flow +++ b/packages/react-native/index.js.flow @@ -279,6 +279,12 @@ export type { } from './Libraries/AppState/AppState'; export {default as AppState} from './Libraries/AppState/AppState'; +export {AssetRegistry} from './src/private/assets/AssetRegistry'; +export type { + AssetDestPathResolver, + PackagerAsset, +} from './src/private/assets/AssetRegistry'; + export type {BackPressEventName} from './Libraries/Utilities/BackHandler'; export {default as BackHandler} from './Libraries/Utilities/BackHandler'; diff --git a/packages/react-native/package.json b/packages/react-native/package.json index ec26182439fe..1f776ee08f43 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -56,6 +56,10 @@ "react-native-strict-api": null, "default": "./types/*.d.ts" }, + "./asset-registry": { + "types": null, + "default": "./src/asset-registry.js" + }, "./jest-preset": "./jest-preset.js", "./rn-get-polyfills": "./rn-get-polyfills.js", "./src/fb_internal/*": "./src/fb_internal/*", diff --git a/packages/react-native/src/asset-registry.js b/packages/react-native/src/asset-registry.js new file mode 100644 index 000000000000..783c998dd5b2 --- /dev/null +++ b/packages/react-native/src/asset-registry.js @@ -0,0 +1,27 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +// ---------------------------------------------------------------------------- +// Secondary react-native/asset-registry entry point. +// +// This is an untyped secondary entry point intended to be referenced from +// Metro's `transformer.assetRegistryPath` config option. This entry point may +// also be preferred in server-side code. +// +// Apps/libraries should use `import {AssetRegistry} from 'react-native'`. +// ---------------------------------------------------------------------------- + +import {AssetRegistry} from './private/assets/AssetRegistry'; + +/* eslint-disable @react-native/monorepo/no-commonjs-exports */ +module.exports = { + registerAsset: AssetRegistry.registerAsset, + getAssetByID: AssetRegistry.getAssetByID, +}; diff --git a/packages/react-native/src/private/assets/AssetRegistry.js b/packages/react-native/src/private/assets/AssetRegistry.js new file mode 100644 index 000000000000..22b716bd37c7 --- /dev/null +++ b/packages/react-native/src/private/assets/AssetRegistry.js @@ -0,0 +1,52 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +export type AssetDestPathResolver = 'android' | 'generic'; + +export type PackagerAsset = Readonly<{ + __packager_asset: boolean, + fileSystemLocation: string, + httpServerLocation: string, + width: ?number, + height: ?number, + scales: Array, + hash: string, + name: string, + type: string, + resolver?: AssetDestPathResolver, + ... +}>; + +const assets: Array = []; + +/** + * Runtime registry that maps asset IDs generated in a Metro bundle to asset + * metadata. It backs ``, `Image.resolveAssetSource()`, and any code + * that resolves `require('./img.png')` on native. + * + * Most apps do not use this directly — assets are handled through ``. + */ +export const AssetRegistry = { + /** + * Register an asset. Returns the asset ID. + */ + registerAsset(asset: PackagerAsset): number { + // `push` returns the new length, so the first asset gets id 1 (not 0), + // keeping ids truthy. + return assets.push(asset); + }, + + /** + * Retrieve a registered asset by ID. + */ + getAssetByID(assetId: number): PackagerAsset { + return assets[assetId - 1]; + }, +}; diff --git a/scripts/shared/monorepoUtils.js b/scripts/shared/monorepoUtils.js index 6260b0a19f70..eecb81250a01 100644 --- a/scripts/shared/monorepoUtils.js +++ b/scripts/shared/monorepoUtils.js @@ -155,7 +155,11 @@ async function updatePackageJson( } for (const dependency in newPackageVersions) { - if (dependency in deps) { + if ( + dependency in deps && + // Preserve wildcard specifiers + deps[dependency] !== '*' + ) { deps[dependency] = newPackageVersions[dependency]; } }