@symbiote-native/asset 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +4 -0
- package/build/angular/services/assets.service/index.d.ts +8 -0
- package/build/angular/services/assets.service/index.js +64 -0
- package/build/core/asset-fx.d.ts +1 -0
- package/build/core/asset-fx.js +24 -0
- package/build/core/asset-sources.d.ts +13 -0
- package/build/core/asset-sources.js +50 -0
- package/build/core/asset-uris.d.ts +3 -0
- package/build/core/asset-uris.js +33 -0
- package/build/core/asset.d.ts +35 -0
- package/build/core/asset.js +158 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/local-assets.d.ts +1 -0
- package/build/core/local-assets.js +16 -0
- package/build/core/native-module.d.ts +4 -0
- package/build/core/native-module.js +2 -0
- package/build/core/platform-utils.d.ts +5 -0
- package/build/core/platform-utils.js +28 -0
- package/build/react/hooks/use-assets/index.d.ts +2 -0
- package/build/react/hooks/use-assets/index.js +14 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.js +3 -0
- package/build/solid/index.d.ts +2 -0
- package/build/solid/index.js +5 -0
- package/build/solid/primitives/create-assets.d.ts +6 -0
- package/build/solid/primitives/create-assets.js +10 -0
- package/build/svelte/index.d.ts +2 -0
- package/build/svelte/index.js +5 -0
- package/build/svelte/runes/use-assets.svelte.d.ts +5 -0
- package/build/svelte/runes/use-assets.svelte.js +24 -0
- package/build/vue/composables/use-assets/index.d.ts +6 -0
- package/build/vue/composables/use-assets/index.js +18 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +3 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/assets.service/index.d.ts +11 -0
- package/build-ngc/angular/services/assets.service/index.js +24 -0
- package/build-ngc/angular/services/assets.service/index.js.map +1 -0
- package/build-ngc/core/asset-fx.d.ts +1 -0
- package/build-ngc/core/asset-fx.js +25 -0
- package/build-ngc/core/asset-fx.js.map +1 -0
- package/build-ngc/core/asset-sources.d.ts +13 -0
- package/build-ngc/core/asset-sources.js +51 -0
- package/build-ngc/core/asset-sources.js.map +1 -0
- package/build-ngc/core/asset-uris.d.ts +3 -0
- package/build-ngc/core/asset-uris.js +34 -0
- package/build-ngc/core/asset-uris.js.map +1 -0
- package/build-ngc/core/asset.d.ts +35 -0
- package/build-ngc/core/asset.js +159 -0
- package/build-ngc/core/asset.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/local-assets.d.ts +1 -0
- package/build-ngc/core/local-assets.js +17 -0
- package/build-ngc/core/local-assets.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +4 -0
- package/build-ngc/core/native-module.js +3 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/platform-utils.d.ts +5 -0
- package/build-ngc/core/platform-utils.js +29 -0
- package/build-ngc/core/platform-utils.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +151 -0
- package/src/angular/index.ts +4 -0
- package/src/angular/services/assets.service/index.ts +21 -0
- package/src/core/asset-fx.ts +30 -0
- package/src/core/asset-sources.ts +80 -0
- package/src/core/asset-uris.ts +51 -0
- package/src/core/asset.ts +212 -0
- package/src/core/index.ts +8 -0
- package/src/core/local-assets.ts +22 -0
- package/src/core/native-module.ts +11 -0
- package/src/core/platform-utils.ts +47 -0
- package/src/core/ts-declarations/react-native-assets.d.ts +75 -0
- package/src/react/hooks/use-assets/index.ts +19 -0
- package/src/react/index.ts +4 -0
- package/src/solid/index.ts +6 -0
- package/src/solid/primitives/create-assets.ts +16 -0
- package/src/svelte/index.ts +6 -0
- package/src/svelte/runes/use-assets.svelte.ts +30 -0
- package/src/svelte/svelte-ambient.d.ts +3 -0
- package/src/vue/composables/use-assets/index.ts +30 -0
- package/src/vue/index.ts +4 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Ported verbatim from expo-asset/src/LocalAssets.ts (sdk-57). localAssets comes from
|
|
2
|
+
// expo-updates (classic-updates/Expo-Go asset overrides) via platform-utils.ts — always `{}`
|
|
3
|
+
// in a bare app that ships neither, so this always falls through to null there.
|
|
4
|
+
import { getLocalAssets } from './platform-utils';
|
|
5
|
+
const localAssets = getLocalAssets();
|
|
6
|
+
export function getLocalAssetUri(hash, type) {
|
|
7
|
+
const localAssetsKey = hash;
|
|
8
|
+
const legacyLocalAssetsKey = `${hash}.${type ?? ''}`;
|
|
9
|
+
if (localAssetsKey in localAssets) {
|
|
10
|
+
return localAssets[localAssetsKey] ?? null;
|
|
11
|
+
}
|
|
12
|
+
if (legacyLocalAssetsKey in localAssets) {
|
|
13
|
+
return localAssets[legacyLocalAssetsKey] ?? null;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=local-assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-assets.js","sourceRoot":"","sources":["../../src/core/local-assets.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,6FAA6F;AAC7F,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;AAErC,MAAM,UAAU,gBAAgB,CAC9B,IAAY,EACZ,IAAmB;IAEnB,MAAM,cAAc,GAAG,IAAI,CAAC;IAC5B,MAAM,oBAAoB,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;IAErD,IAAI,cAAc,IAAI,WAAW,EAAE,CAAC;QAClC,OAAO,WAAW,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC;IAC7C,CAAC;IACD,IAAI,oBAAoB,IAAI,WAAW,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC,oBAAoB,CAAC,IAAI,IAAI,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAUxD,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAqB,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import Constants from 'expo-constants';
|
|
2
|
+
export declare const IS_ENV_WITH_LOCAL_ASSETS: boolean;
|
|
3
|
+
export declare function getLocalAssets(): Record<string, string>;
|
|
4
|
+
export declare function getManifest2(): typeof Constants.__unsafeNoWarnManifest2;
|
|
5
|
+
export declare const manifestBaseUrl: string | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Ported from expo-asset/src/PlatformUtils.ts (sdk-57) — real logic, not a stub. Every check
|
|
2
|
+
// here (ExpoGo/expo-updates native modules, the classic-updates manifest) naturally resolves to
|
|
3
|
+
// "absent" in a bare app that ships neither, matching upstream's own designed fallback.
|
|
4
|
+
import Constants from 'expo-constants';
|
|
5
|
+
import { requireNativeModule, requireOptionalNativeModule, } from 'expo-modules-core';
|
|
6
|
+
import { getManifestBaseUrl } from './asset-uris';
|
|
7
|
+
const ExpoUpdates = requireOptionalNativeModule('ExpoUpdates');
|
|
8
|
+
function isRunningInExpoGo() {
|
|
9
|
+
try {
|
|
10
|
+
return requireNativeModule('ExpoGo') != null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const expoUpdatesIsInstalledAndEnabled = !!ExpoUpdates?.isEnabled;
|
|
17
|
+
const expoUpdatesIsUsingEmbeddedAssets = ExpoUpdates?.isUsingEmbeddedAssets;
|
|
18
|
+
const shouldUseUpdatesAssetResolution = expoUpdatesIsInstalledAndEnabled && !expoUpdatesIsUsingEmbeddedAssets;
|
|
19
|
+
export const IS_ENV_WITH_LOCAL_ASSETS = isRunningInExpoGo() || shouldUseUpdatesAssetResolution;
|
|
20
|
+
export function getLocalAssets() {
|
|
21
|
+
return ExpoUpdates?.localAssets ?? {};
|
|
22
|
+
}
|
|
23
|
+
export function getManifest2() {
|
|
24
|
+
return Constants.__unsafeNoWarnManifest2;
|
|
25
|
+
}
|
|
26
|
+
export const manifestBaseUrl = Constants.experienceUrl
|
|
27
|
+
? getManifestBaseUrl(Constants.experienceUrl)
|
|
28
|
+
: null;
|
|
29
|
+
//# sourceMappingURL=platform-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-utils.js","sourceRoot":"","sources":["../../src/core/platform-utils.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,gGAAgG;AAChG,wFAAwF;AACxF,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,EACL,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,GACf,2BAA2B,CAAqB,aAAa,CAAC,CAAC;AAEjE,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,OAAO,mBAAmB,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,gCAAgC,GAAG,CAAC,CAAC,WAAW,EAAE,SAAS,CAAC;AAClE,MAAM,gCAAgC,GAAG,WAAW,EAAE,qBAAqB,CAAC;AAC5E,MAAM,+BAA+B,GACnC,gCAAgC,IAAI,CAAC,gCAAgC,CAAC;AAExE,MAAM,CAAC,MAAM,wBAAwB,GACnC,iBAAiB,EAAE,IAAI,+BAA+B,CAAC;AAEzD,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,EAAE,WAAW,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,SAAS,CAAC,uBAAuB,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC,aAAa;IACpD,CAAC,CAAC,kBAAkB,CAAC,SAAS,CAAC,aAAa,CAAC;IAC7C,CAAC,CAAC,IAAI,CAAC"}
|
package/native-link.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@symbiote-native/asset",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "expo-asset wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Resolves bundled/remote assets by require() module id or URI, and downloads them to a local cache file.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/OneEyed1366/symbiote-native.git",
|
|
9
|
+
"directory": "packages/asset"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/asset#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
|
+
},
|
|
15
|
+
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"expo-asset",
|
|
20
|
+
"react",
|
|
21
|
+
"vue",
|
|
22
|
+
"svelte",
|
|
23
|
+
"solid",
|
|
24
|
+
"angular",
|
|
25
|
+
"asset"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./build/core/index.js",
|
|
29
|
+
"module": "./build/core/index.js",
|
|
30
|
+
"types": "./build/core/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./build/core/index.d.ts",
|
|
34
|
+
"default": "./build/core/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./vue": {
|
|
37
|
+
"types": "./build/vue/index.d.ts",
|
|
38
|
+
"default": "./build/vue/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./react": {
|
|
41
|
+
"types": "./build/react/index.d.ts",
|
|
42
|
+
"default": "./build/react/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./svelte": {
|
|
45
|
+
"types": "./build/svelte/index.d.ts",
|
|
46
|
+
"default": "./build/svelte/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./solid": {
|
|
49
|
+
"types": "./build/solid/index.d.ts",
|
|
50
|
+
"default": "./build/solid/index.js"
|
|
51
|
+
},
|
|
52
|
+
"./angular": {
|
|
53
|
+
"types": "./build-ngc/angular/index.d.ts",
|
|
54
|
+
"react-native": "./build-ngc/angular/index.js",
|
|
55
|
+
"default": "./src/angular/index.ts"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"files": [
|
|
59
|
+
"src",
|
|
60
|
+
"build",
|
|
61
|
+
"build-ngc",
|
|
62
|
+
"native-link.json",
|
|
63
|
+
"!src/**/*.test.*",
|
|
64
|
+
"!src/**/*.spec.*",
|
|
65
|
+
"!src/**/*.detox.*"
|
|
66
|
+
],
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"expo-asset": "57.0.18",
|
|
72
|
+
"expo-modules-core": "57.0.20",
|
|
73
|
+
"expo-constants": "57.0.19",
|
|
74
|
+
"@react-native/assets-registry": "0.86.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@angular/core": ">=20",
|
|
78
|
+
"@vue/runtime-core": "^3.5.13",
|
|
79
|
+
"react": ">=19.0.0",
|
|
80
|
+
"react-native": ">=0.86",
|
|
81
|
+
"solid-js": ">=1.9.0",
|
|
82
|
+
"svelte": ">=5.56.0",
|
|
83
|
+
"vue": ">=3.5.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.4.0",
|
|
85
|
+
"@symbiote-native/angular": "^3.1.3",
|
|
86
|
+
"@symbiote-native/react": "^3.1.0",
|
|
87
|
+
"@symbiote-native/solid": "^3.0.5",
|
|
88
|
+
"@symbiote-native/svelte": "^3.0.5",
|
|
89
|
+
"@symbiote-native/vue": "^3.1.0"
|
|
90
|
+
},
|
|
91
|
+
"peerDependenciesMeta": {
|
|
92
|
+
"@symbiote-native/angular": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@symbiote-native/react": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@symbiote-native/solid": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"@symbiote-native/svelte": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@symbiote-native/vue": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@angular/core": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@vue/runtime-core": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"react": {
|
|
114
|
+
"optional": true
|
|
115
|
+
},
|
|
116
|
+
"solid-js": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
119
|
+
"svelte": {
|
|
120
|
+
"optional": true
|
|
121
|
+
},
|
|
122
|
+
"vue": {
|
|
123
|
+
"optional": true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"devDependencies": {
|
|
127
|
+
"@angular/compiler": "~22.0.8",
|
|
128
|
+
"@angular/compiler-cli": "~22.0.8",
|
|
129
|
+
"@angular/core": "~22.0.8",
|
|
130
|
+
"@types/node": "^26.0.0",
|
|
131
|
+
"@types/react": "^19.2.0",
|
|
132
|
+
"@vue/runtime-core": "^3.5.13",
|
|
133
|
+
"react": "19.2.3",
|
|
134
|
+
"solid-js": "^1.9.14",
|
|
135
|
+
"svelte": "^5.56.0",
|
|
136
|
+
"typescript": "~6.0.0",
|
|
137
|
+
"@symbiote-native/angular": "3.1.3",
|
|
138
|
+
"@symbiote-native/react": "3.1.0",
|
|
139
|
+
"@symbiote-native/solid": "3.0.5",
|
|
140
|
+
"@symbiote-native/svelte": "3.0.5",
|
|
141
|
+
"@symbiote-native/engine": "1.4.0",
|
|
142
|
+
"@symbiote-native/vue": "3.1.0",
|
|
143
|
+
"@symbiote-native/test-utils": "0.4.5"
|
|
144
|
+
},
|
|
145
|
+
"scripts": {
|
|
146
|
+
"typecheck": "tsc --build",
|
|
147
|
+
"clean": "rm -rf build-ngc",
|
|
148
|
+
"ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
|
|
149
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Injectable, signal, type Signal } from '@angular/core';
|
|
2
|
+
import { Asset } from '../../../core';
|
|
3
|
+
|
|
4
|
+
// Angular twin of React's useAssets hook and Vue's useAssets composable — connect(ids) returns
|
|
5
|
+
// a Signal pair, e.g. `readonly assets = inject(AssetsService).connect([require('./a.jpg')])`.
|
|
6
|
+
@Injectable({ providedIn: 'root' })
|
|
7
|
+
export class AssetsService {
|
|
8
|
+
connect(moduleIds: number | number[]): {
|
|
9
|
+
assets: Signal<Asset[] | undefined>;
|
|
10
|
+
error: Signal<Error | undefined>;
|
|
11
|
+
} {
|
|
12
|
+
const assets = signal<Asset[] | undefined>(undefined);
|
|
13
|
+
const error = signal<Error | undefined>(undefined);
|
|
14
|
+
|
|
15
|
+
Asset.loadAsync(moduleIds)
|
|
16
|
+
.then(loaded => assets.set(loaded))
|
|
17
|
+
.catch((err: Error) => error.set(err));
|
|
18
|
+
|
|
19
|
+
return { assets: assets.asReadonly(), error: error.asReadonly() };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Ported from expo-asset/src/Asset.fx.ts (sdk-57). Overrides RN's own Image asset resolution
|
|
2
|
+
// for a classic-updates/Expo-Go env — a no-op import in a bare app (IS_ENV_WITH_LOCAL_ASSETS
|
|
3
|
+
// is false), imported for its side effect from core/index.ts, matching upstream's own index.ts.
|
|
4
|
+
import resolveAssetSource, {
|
|
5
|
+
type ICustomSourceTransformer,
|
|
6
|
+
} from 'react-native/Libraries/Image/resolveAssetSource';
|
|
7
|
+
|
|
8
|
+
import { Asset, ANDROID_EMBEDDED_URL_BASE_RESOURCE } from './asset';
|
|
9
|
+
import { IS_ENV_WITH_LOCAL_ASSETS } from './platform-utils';
|
|
10
|
+
|
|
11
|
+
const expoAssetTransformer: ICustomSourceTransformer = resolver => {
|
|
12
|
+
try {
|
|
13
|
+
if ('fileHashes' in resolver.asset && resolver.asset.fileHashes) {
|
|
14
|
+
const asset = Asset.fromMetadata(resolver.asset);
|
|
15
|
+
if (asset.uri.startsWith(ANDROID_EMBEDDED_URL_BASE_RESOURCE)) {
|
|
16
|
+
return resolver.resourceIdentifierWithoutScale();
|
|
17
|
+
}
|
|
18
|
+
return resolver.fromSource(
|
|
19
|
+
asset.downloaded && asset.localUri ? asset.localUri : asset.uri,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return resolver.defaultAsset();
|
|
23
|
+
} catch {
|
|
24
|
+
return resolver.defaultAsset();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (IS_ENV_WITH_LOCAL_ASSETS) {
|
|
29
|
+
resolveAssetSource.setCustomSourceTransformer?.(expoAssetTransformer);
|
|
30
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Ported from expo-asset/src/AssetSources.ts (sdk-57) — full parity, manifest2 branch included.
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
3
|
+
/// <reference path="./ts-declarations/react-native-assets.d.ts" />
|
|
4
|
+
import type { PackagerAsset } from '@react-native/assets-registry/registry';
|
|
5
|
+
import { Platform } from 'expo-modules-core';
|
|
6
|
+
import { NativeModules, PixelRatio } from 'react-native';
|
|
7
|
+
import AssetSourceResolver from 'react-native/Libraries/Image/AssetSourceResolver';
|
|
8
|
+
|
|
9
|
+
import { getManifest2, manifestBaseUrl } from './platform-utils';
|
|
10
|
+
|
|
11
|
+
export type AssetMetadata = Pick<
|
|
12
|
+
PackagerAsset,
|
|
13
|
+
| 'httpServerLocation'
|
|
14
|
+
| 'name'
|
|
15
|
+
| 'hash'
|
|
16
|
+
| 'type'
|
|
17
|
+
| 'scales'
|
|
18
|
+
| 'width'
|
|
19
|
+
| 'height'
|
|
20
|
+
> & {
|
|
21
|
+
uri?: string;
|
|
22
|
+
fileHashes?: string[];
|
|
23
|
+
fileUris?: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type AssetSource = { uri: string; hash: string };
|
|
27
|
+
|
|
28
|
+
/** Resolves a relative asset URI against the classic-updates manifest base, if any. */
|
|
29
|
+
export function resolveUri(uri: string): string {
|
|
30
|
+
return manifestBaseUrl ? new URL(uri, manifestBaseUrl).href : uri;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function selectAssetSource(meta: AssetMetadata): AssetSource {
|
|
34
|
+
const scale = AssetSourceResolver.pickScale(meta.scales, PixelRatio.get());
|
|
35
|
+
const index = meta.scales.findIndex(s => s === scale);
|
|
36
|
+
const hash = meta.fileHashes?.[index] ?? meta.fileHashes?.[0] ?? meta.hash;
|
|
37
|
+
|
|
38
|
+
const uri = meta.fileUris
|
|
39
|
+
? (meta.fileUris[index] ?? meta.fileUris[0])
|
|
40
|
+
: meta.uri;
|
|
41
|
+
if (uri) {
|
|
42
|
+
return { uri: resolveUri(uri), hash };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const fileScale = scale === 1 ? '' : `@${scale}x`;
|
|
46
|
+
const fileExtension = meta.type ? `.${encodeURIComponent(meta.type)}` : '';
|
|
47
|
+
const suffix = `/${encodeURIComponent(meta.name)}${fileScale}${fileExtension}`;
|
|
48
|
+
const params = new URLSearchParams({
|
|
49
|
+
platform: Platform.OS,
|
|
50
|
+
hash: meta.hash,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (/^https?:\/\//.test(meta.httpServerLocation)) {
|
|
54
|
+
return { uri: meta.httpServerLocation + suffix + '?' + params, hash };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const manifest2 = getManifest2();
|
|
58
|
+
const scheme = manifestBaseUrl?.startsWith('https://')
|
|
59
|
+
? 'https://'
|
|
60
|
+
: 'http://';
|
|
61
|
+
const devServerUrl = manifest2?.extra?.expoGo?.developer
|
|
62
|
+
? scheme + manifest2.extra.expoGo.debuggerHost
|
|
63
|
+
: null;
|
|
64
|
+
if (devServerUrl) {
|
|
65
|
+
const baseUrl = new URL(meta.httpServerLocation + suffix, devServerUrl);
|
|
66
|
+
baseUrl.searchParams.set('platform', Platform.OS);
|
|
67
|
+
baseUrl.searchParams.set('hash', meta.hash);
|
|
68
|
+
return { uri: baseUrl.href, hash };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (NativeModules['ExponentKernel']) {
|
|
72
|
+
return {
|
|
73
|
+
uri: `https://classic-assets.eascdn.net/~assets/${encodeURIComponent(hash)}`,
|
|
74
|
+
hash,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Locally available on disk (classic-updates embedded asset): no remote URL to build.
|
|
79
|
+
return { uri: '', hash };
|
|
80
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Ported from expo-asset/src/AssetUris.ts (sdk-57).
|
|
2
|
+
|
|
3
|
+
// Read via globalThis, matching packages/background-task's own __DEV__ convention (no ambient
|
|
4
|
+
// declare — react-native is a peer, never imported here).
|
|
5
|
+
type IDevGlobal = { __DEV__?: boolean };
|
|
6
|
+
function isDevBuild(): boolean {
|
|
7
|
+
return Boolean((globalThis as IDevGlobal).__DEV__);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getBasename(pathname: string): string {
|
|
11
|
+
return pathname.substring(pathname.lastIndexOf('/') + 1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getFilename(url: string): string {
|
|
15
|
+
const { pathname, searchParams } = new URL(url, 'https://e');
|
|
16
|
+
|
|
17
|
+
if (isDevBuild() && searchParams.has('unstable_path')) {
|
|
18
|
+
const encodedFilePath = decodeURIComponent(
|
|
19
|
+
searchParams.get('unstable_path')!,
|
|
20
|
+
);
|
|
21
|
+
return getBasename(encodedFilePath);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return getBasename(pathname);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getFileExtension(url: string): string {
|
|
28
|
+
const filename = getFilename(url);
|
|
29
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
30
|
+
return dotIndex > 0 ? filename.substring(dotIndex) : '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Rebuilds the URL from parts rather than mutating `protocol`/`pathname` in place: react-native's
|
|
34
|
+
// own global.d.ts types those fields readonly (its URL polyfill's actual shape), conflicting with
|
|
35
|
+
// @types/node's writable declaration wherever both are ambient — same net result either way.
|
|
36
|
+
export function getManifestBaseUrl(manifestUrl: string): string {
|
|
37
|
+
const urlObject = new URL(manifestUrl);
|
|
38
|
+
|
|
39
|
+
const protocol =
|
|
40
|
+
urlObject.protocol === 'exp:'
|
|
41
|
+
? 'http:'
|
|
42
|
+
: urlObject.protocol === 'exps:'
|
|
43
|
+
? 'https:'
|
|
44
|
+
: urlObject.protocol;
|
|
45
|
+
const directory = urlObject.pathname.substring(
|
|
46
|
+
0,
|
|
47
|
+
urlObject.pathname.lastIndexOf('/') + 1,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return `${protocol}//${urlObject.host}${directory}`;
|
|
51
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Ported from expo-asset/src/Asset.ts (sdk-57), native-only, no Expo Go/expo-updates.
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
3
|
+
/// <reference path="./ts-declarations/react-native-assets.d.ts" />
|
|
4
|
+
import { getAssetByID } from '@react-native/assets-registry/registry';
|
|
5
|
+
import { Platform } from 'expo-modules-core';
|
|
6
|
+
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
|
|
7
|
+
|
|
8
|
+
import { type AssetMetadata, selectAssetSource } from './asset-sources';
|
|
9
|
+
import { getFileExtension } from './asset-uris';
|
|
10
|
+
import { getLocalAssetUri } from './local-assets';
|
|
11
|
+
import { expoAsset } from './native-module';
|
|
12
|
+
import { IS_ENV_WITH_LOCAL_ASSETS } from './platform-utils';
|
|
13
|
+
|
|
14
|
+
export const ANDROID_EMBEDDED_URL_BASE_RESOURCE = 'file:///android_res/';
|
|
15
|
+
|
|
16
|
+
export type AssetDescriptor = {
|
|
17
|
+
name: string;
|
|
18
|
+
type: string;
|
|
19
|
+
hash?: string | null;
|
|
20
|
+
uri: string;
|
|
21
|
+
width?: number | null;
|
|
22
|
+
height?: number | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type IDownloadPromiseCallbacks = {
|
|
26
|
+
resolve: () => void;
|
|
27
|
+
reject: (error: Error) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** A locally- or remotely-sourced asset: metadata plus loading/caching facilities. */
|
|
31
|
+
export class Asset {
|
|
32
|
+
private static byHash: Record<string, Asset | undefined> = {};
|
|
33
|
+
private static byUri: Record<string, Asset | undefined> = {};
|
|
34
|
+
|
|
35
|
+
public name: string;
|
|
36
|
+
public readonly type: string;
|
|
37
|
+
public readonly hash: string | null = null;
|
|
38
|
+
public readonly uri: string;
|
|
39
|
+
public localUri: string | null = null;
|
|
40
|
+
public width: number | null = null;
|
|
41
|
+
public height: number | null = null;
|
|
42
|
+
public downloaded: boolean = false;
|
|
43
|
+
|
|
44
|
+
private downloading: boolean = false;
|
|
45
|
+
private downloadCallbacks: IDownloadPromiseCallbacks[] = [];
|
|
46
|
+
|
|
47
|
+
constructor({
|
|
48
|
+
name,
|
|
49
|
+
type,
|
|
50
|
+
hash = null,
|
|
51
|
+
uri,
|
|
52
|
+
width,
|
|
53
|
+
height,
|
|
54
|
+
}: AssetDescriptor) {
|
|
55
|
+
this.name = name;
|
|
56
|
+
this.type = type;
|
|
57
|
+
this.hash = hash;
|
|
58
|
+
this.uri = uri;
|
|
59
|
+
|
|
60
|
+
if (typeof width === 'number') this.width = width;
|
|
61
|
+
if (typeof height === 'number') this.height = height;
|
|
62
|
+
|
|
63
|
+
if (hash) {
|
|
64
|
+
this.localUri = getLocalAssetUri(hash, type);
|
|
65
|
+
if (this.localUri?.startsWith(ANDROID_EMBEDDED_URL_BASE_RESOURCE)) {
|
|
66
|
+
this.uri = this.localUri;
|
|
67
|
+
this.localUri = null;
|
|
68
|
+
} else if (this.localUri) {
|
|
69
|
+
this.downloaded = true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static loadAsync(
|
|
75
|
+
moduleId: number | number[] | string | string[],
|
|
76
|
+
): Promise<Asset[]> {
|
|
77
|
+
const moduleIds = Array.isArray(moduleId) ? moduleId : [moduleId];
|
|
78
|
+
return Promise.all(
|
|
79
|
+
moduleIds.map(id => Asset.fromModule(id).downloadAsync()),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static fromModule(
|
|
84
|
+
virtualAssetModule:
|
|
85
|
+
number | string | { uri: string; width: number; height: number },
|
|
86
|
+
): Asset {
|
|
87
|
+
if (typeof virtualAssetModule === 'string') {
|
|
88
|
+
return Asset.fromURI(virtualAssetModule);
|
|
89
|
+
}
|
|
90
|
+
if (
|
|
91
|
+
typeof virtualAssetModule === 'object' &&
|
|
92
|
+
'uri' in virtualAssetModule &&
|
|
93
|
+
typeof virtualAssetModule.uri === 'string'
|
|
94
|
+
) {
|
|
95
|
+
const extension = getFileExtension(virtualAssetModule.uri);
|
|
96
|
+
return new Asset({
|
|
97
|
+
name: '',
|
|
98
|
+
type: extension.startsWith('.') ? extension.substring(1) : extension,
|
|
99
|
+
hash: null,
|
|
100
|
+
uri: virtualAssetModule.uri,
|
|
101
|
+
width: virtualAssetModule.width,
|
|
102
|
+
height: virtualAssetModule.height,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const meta = getAssetByID(virtualAssetModule);
|
|
107
|
+
if (!meta) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Module "${virtualAssetModule}" is missing from the asset registry`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Outside a managed (Expo Go / expo-updates) env, the moduleId is needed because
|
|
114
|
+
// resolveAssetSource depends on it — a classic-updates env resolves by hash instead.
|
|
115
|
+
if (!IS_ENV_WITH_LOCAL_ASSETS) {
|
|
116
|
+
const resolved = resolveAssetSource(virtualAssetModule);
|
|
117
|
+
if (!resolved) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Module "${virtualAssetModule}" could not be resolved to an asset source`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const asset = new Asset({
|
|
124
|
+
name: meta.name,
|
|
125
|
+
type: meta.type,
|
|
126
|
+
hash: meta.hash,
|
|
127
|
+
uri: resolved.uri,
|
|
128
|
+
width: meta.width,
|
|
129
|
+
height: meta.height,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Android drawable resource names (no scheme) are already usable directly, same as RN's
|
|
133
|
+
// own Image component backward-compat handling.
|
|
134
|
+
if (
|
|
135
|
+
Platform.OS === 'android' &&
|
|
136
|
+
!resolved.uri.includes(':') &&
|
|
137
|
+
(meta.width || meta.height)
|
|
138
|
+
) {
|
|
139
|
+
asset.localUri = asset.uri;
|
|
140
|
+
asset.downloaded = true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Asset.byHash[meta.hash] = asset;
|
|
144
|
+
return asset;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Asset.fromMetadata(meta);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static fromMetadata(meta: AssetMetadata): Asset {
|
|
151
|
+
const existing = Asset.byHash[meta.hash];
|
|
152
|
+
if (existing) return existing;
|
|
153
|
+
|
|
154
|
+
const { uri, hash } = selectAssetSource(meta);
|
|
155
|
+
const asset = new Asset({
|
|
156
|
+
name: meta.name,
|
|
157
|
+
type: meta.type,
|
|
158
|
+
hash,
|
|
159
|
+
uri,
|
|
160
|
+
width: meta.width,
|
|
161
|
+
height: meta.height,
|
|
162
|
+
});
|
|
163
|
+
Asset.byHash[meta.hash] = asset;
|
|
164
|
+
return asset;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static fromURI(uri: string): Asset {
|
|
168
|
+
const existing = Asset.byUri[uri];
|
|
169
|
+
if (existing) return existing;
|
|
170
|
+
|
|
171
|
+
let type: string;
|
|
172
|
+
if (uri.indexOf(';base64') > -1) {
|
|
173
|
+
type = uri.split(';')[0]?.split('/')[1] ?? '';
|
|
174
|
+
} else {
|
|
175
|
+
const extension = getFileExtension(uri);
|
|
176
|
+
type = extension.startsWith('.') ? extension.substring(1) : extension;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const asset = new Asset({ name: '', type, hash: null, uri });
|
|
180
|
+
Asset.byUri[uri] = asset;
|
|
181
|
+
return asset;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async downloadAsync(): Promise<this> {
|
|
185
|
+
if (this.downloaded) return this;
|
|
186
|
+
if (this.downloading) {
|
|
187
|
+
await new Promise<void>((resolve, reject) => {
|
|
188
|
+
this.downloadCallbacks.push({ resolve, reject });
|
|
189
|
+
});
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
this.downloading = true;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
this.localUri = await expoAsset.downloadAsync(
|
|
196
|
+
this.uri,
|
|
197
|
+
this.hash,
|
|
198
|
+
this.type,
|
|
199
|
+
);
|
|
200
|
+
this.downloaded = true;
|
|
201
|
+
this.downloadCallbacks.forEach(({ resolve }) => resolve());
|
|
202
|
+
} catch (error) {
|
|
203
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
204
|
+
this.downloadCallbacks.forEach(({ reject }) => reject(reason));
|
|
205
|
+
throw reason;
|
|
206
|
+
} finally {
|
|
207
|
+
this.downloading = false;
|
|
208
|
+
this.downloadCallbacks = [];
|
|
209
|
+
}
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Ported verbatim from expo-asset/src/LocalAssets.ts (sdk-57). localAssets comes from
|
|
2
|
+
// expo-updates (classic-updates/Expo-Go asset overrides) via platform-utils.ts — always `{}`
|
|
3
|
+
// in a bare app that ships neither, so this always falls through to null there.
|
|
4
|
+
import { getLocalAssets } from './platform-utils';
|
|
5
|
+
|
|
6
|
+
const localAssets = getLocalAssets();
|
|
7
|
+
|
|
8
|
+
export function getLocalAssetUri(
|
|
9
|
+
hash: string,
|
|
10
|
+
type: string | null,
|
|
11
|
+
): string | null {
|
|
12
|
+
const localAssetsKey = hash;
|
|
13
|
+
const legacyLocalAssetsKey = `${hash}.${type ?? ''}`;
|
|
14
|
+
|
|
15
|
+
if (localAssetsKey in localAssets) {
|
|
16
|
+
return localAssets[localAssetsKey] ?? null;
|
|
17
|
+
}
|
|
18
|
+
if (legacyLocalAssetsKey in localAssets) {
|
|
19
|
+
return localAssets[legacyLocalAssetsKey] ?? null;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|