expo-image 0.1.0 → 0.2.2
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/README.md +3 -3
- package/android/build.gradle +30 -20
- package/android/src/main/java/expo/modules/image/okhttp/OkHttpClientProgressInterceptor.kt +2 -2
- package/build/ExpoImage.d.ts +5 -0
- package/build/ExpoImage.d.ts.map +1 -0
- package/build/ExpoImage.js +58 -0
- package/build/ExpoImage.js.map +1 -0
- package/build/ExpoImage.web.d.ts +3 -0
- package/build/ExpoImage.web.d.ts.map +1 -0
- package/build/ExpoImage.web.js +8 -0
- package/build/ExpoImage.web.js.map +1 -0
- package/build/Image.d.ts +45 -0
- package/build/Image.d.ts.map +1 -0
- package/build/Image.js +48 -0
- package/build/Image.js.map +1 -0
- package/build/Image.types.d.ts +42 -0
- package/build/Image.types.d.ts.map +1 -0
- package/build/Image.types.js +8 -0
- package/build/Image.types.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ A cross-platform, performant image component for React Native and Expo with Web
|
|
|
4
4
|
|
|
5
5
|
# API documentation
|
|
6
6
|
|
|
7
|
-
- [Documentation for the
|
|
8
|
-
- [Documentation for the latest stable release](https://docs.expo.
|
|
7
|
+
- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/image.md)
|
|
8
|
+
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/image/)
|
|
9
9
|
|
|
10
10
|
# Installation in managed Expo projects
|
|
11
11
|
|
|
12
|
-
For
|
|
12
|
+
For [managed](https://docs.expo.dev/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/image/). If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.
|
|
13
13
|
|
|
14
14
|
# Installation in bare React Native projects
|
|
15
15
|
|
package/android/build.gradle
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
def DEFAULT_COMPILE_SDK_VERSION = 28
|
|
2
|
-
def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3'
|
|
3
|
-
def DEFAULT_MIN_SDK_VERSION = 16
|
|
4
|
-
def DEFAULT_TARGET_SDK_VERSION = 28
|
|
5
|
-
|
|
6
|
-
def DEFAULT_OKHTTP_VERSION = '3.14.9'
|
|
7
|
-
|
|
8
1
|
apply plugin: 'com.android.library'
|
|
9
2
|
apply plugin: 'kotlin-android'
|
|
10
|
-
apply plugin: 'maven'
|
|
3
|
+
apply plugin: 'maven-publish'
|
|
11
4
|
apply plugin: 'kotlin-kapt'
|
|
12
5
|
|
|
13
6
|
buildscript {
|
|
7
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
8
|
+
if (expoModulesCorePlugin.exists()) {
|
|
9
|
+
apply from: expoModulesCorePlugin
|
|
10
|
+
applyKotlinExpoModulesCorePlugin()
|
|
11
|
+
}
|
|
12
|
+
|
|
14
13
|
// Simple helper that allows the root project to override versions declared by this library.
|
|
15
14
|
ext.safeExtGet = { prop, fallback ->
|
|
16
15
|
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
17
16
|
}
|
|
18
17
|
|
|
18
|
+
// Ensures backward compatibility
|
|
19
|
+
ext.getKotlinVersion = {
|
|
20
|
+
if (ext.has("kotlinVersion")) {
|
|
21
|
+
ext.kotlinVersion()
|
|
22
|
+
} else {
|
|
23
|
+
ext.safeExtGet("kotlinVersion", "1.6.10")
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
// The Android Gradle plugin is only required when opening the android folder stand-alone.
|
|
20
28
|
// This avoids unnecessary downloads and potential conflicts when the library is included as a
|
|
21
29
|
// module dependency in an application project.
|
|
@@ -23,29 +31,31 @@ buildscript {
|
|
|
23
31
|
if (project == rootProject) {
|
|
24
32
|
repositories {
|
|
25
33
|
google()
|
|
26
|
-
jcenter()
|
|
27
34
|
}
|
|
28
35
|
dependencies {
|
|
29
|
-
classpath 'com.android.tools.build:gradle:
|
|
30
|
-
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${
|
|
36
|
+
classpath 'com.android.tools.build:gradle:4.2.2'
|
|
37
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
android {
|
|
36
|
-
compileSdkVersion safeExtGet(
|
|
37
|
-
buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
|
|
43
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 31)
|
|
38
44
|
|
|
39
45
|
compileOptions {
|
|
40
|
-
sourceCompatibility JavaVersion.
|
|
41
|
-
targetCompatibility JavaVersion.
|
|
46
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
47
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
kotlinOptions {
|
|
51
|
+
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
defaultConfig {
|
|
45
|
-
minSdkVersion safeExtGet(
|
|
46
|
-
targetSdkVersion safeExtGet(
|
|
55
|
+
minSdkVersion safeExtGet("minSdkVersion", 21)
|
|
56
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 31)
|
|
47
57
|
versionCode 1
|
|
48
|
-
versionName "0.
|
|
58
|
+
versionName "0.2.2"
|
|
49
59
|
}
|
|
50
60
|
lintOptions {
|
|
51
61
|
abortOnError false
|
|
@@ -89,9 +99,9 @@ dependencies {
|
|
|
89
99
|
api 'com.caverock:androidsvg-aar:1.4'
|
|
90
100
|
|
|
91
101
|
api 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
|
|
92
|
-
api "com.squareup.okhttp3:okhttp:${safeExtGet("okHttpVersion",
|
|
102
|
+
api "com.squareup.okhttp3:okhttp:${safeExtGet("okHttpVersion", '4.9.2')}"
|
|
93
103
|
|
|
94
104
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
|
|
95
|
-
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${
|
|
105
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
|
96
106
|
implementation "jp.wasabeef:glide-transformations:4.3.0"
|
|
97
107
|
}
|
|
@@ -20,10 +20,10 @@ object OkHttpClientProgressInterceptor : Interceptor {
|
|
|
20
20
|
@Throws(IOException::class)
|
|
21
21
|
override fun intercept(chain: Interceptor.Chain): Response {
|
|
22
22
|
val weakThis = WeakReference(this)
|
|
23
|
-
val requestUrl = chain.call().request().url
|
|
23
|
+
val requestUrl = chain.call().request().url.toString()
|
|
24
24
|
val originalResponse = chain.proceed(chain.request())
|
|
25
25
|
return originalResponse.newBuilder()
|
|
26
|
-
.body(ProgressResponseBody(originalResponse.body
|
|
26
|
+
.body(ProgressResponseBody(originalResponse.body) { bytesWritten, contentLength, done ->
|
|
27
27
|
val strongThis = weakThis.get() ?: return@ProgressResponseBody
|
|
28
28
|
val urlListeners = strongThis.mProgressListeners[requestUrl]
|
|
29
29
|
urlListeners?.forEach { it.get()?.onProgress(bytesWritten, contentLength, done) }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ImageProps } from './Image';
|
|
2
|
+
declare const ExpoImageModule: any;
|
|
3
|
+
export { ExpoImageModule };
|
|
4
|
+
export default function ExpoImage({ source, style, defaultSource, loadingIndicatorSource, ...props }: ImageProps): JSX.Element;
|
|
5
|
+
//# sourceMappingURL=ExpoImage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImage.d.ts","sourceRoot":"","sources":["../src/ExpoImage.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC,QAAA,MAAM,eAAe,KAAgC,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,MAAM,EACN,KAAK,EACL,aAAa,EACb,sBAAsB,EACtB,GAAG,KAAK,EACT,EAAE,UAAU,eAoEZ"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Image, requireNativeComponent, NativeModules, StyleSheet, Platform, processColor, } from 'react-native';
|
|
3
|
+
const NativeExpoImage = requireNativeComponent('ExpoImage');
|
|
4
|
+
const ExpoImageModule = NativeModules.ExpoImageModule;
|
|
5
|
+
export { ExpoImageModule };
|
|
6
|
+
export default function ExpoImage({ source, style, defaultSource, loadingIndicatorSource, ...props }) {
|
|
7
|
+
const resolvedSource = Image.resolveAssetSource(source ?? {});
|
|
8
|
+
const resolvedStyle = StyleSheet.flatten([style]);
|
|
9
|
+
const resolvedPlaceholder = Image.resolveAssetSource(defaultSource ?? loadingIndicatorSource ?? {});
|
|
10
|
+
// If both are specified, we default to use default source
|
|
11
|
+
if (defaultSource && loadingIndicatorSource) {
|
|
12
|
+
console.warn("<Image> component can't have both defaultSource and loadingIndicatorSource at the same time. Defaulting to defaultSource");
|
|
13
|
+
}
|
|
14
|
+
// When possible, pass through the intrinsic size of the asset to the Yoga layout
|
|
15
|
+
// system. While this is also possible in native code, doing it here is more efficient
|
|
16
|
+
// as the yoga node gets initialized with the correct size from the start.
|
|
17
|
+
// In native code, there is a separation between the layout (shadow) nodes and
|
|
18
|
+
// actual views. Views that update the intrinsic content-size in Yoga trigger
|
|
19
|
+
// additional layout passes, which we want to prevent.
|
|
20
|
+
if (!Array.isArray(resolvedSource)) {
|
|
21
|
+
const { width, height } = resolvedSource;
|
|
22
|
+
resolvedStyle.width = resolvedStyle.width ?? width;
|
|
23
|
+
resolvedStyle.height = resolvedStyle.height ?? height;
|
|
24
|
+
}
|
|
25
|
+
// Shadows behave different on iOS, Android & Web.
|
|
26
|
+
// Android uses the `elevation` prop, whereas iOS
|
|
27
|
+
// and web use the regular `shadow...` props.
|
|
28
|
+
let hasShadows = false;
|
|
29
|
+
if (Platform.OS === 'android') {
|
|
30
|
+
delete resolvedStyle.shadowColor;
|
|
31
|
+
delete resolvedStyle.shadowOffset;
|
|
32
|
+
delete resolvedStyle.shadowOpacity;
|
|
33
|
+
delete resolvedStyle.shadowRadius;
|
|
34
|
+
hasShadows = !!resolvedStyle.elevation;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
delete resolvedStyle.elevation;
|
|
38
|
+
hasShadows = !!resolvedStyle.shadowColor;
|
|
39
|
+
}
|
|
40
|
+
// Shadows are rendered quite differently on iOS, Android and web.
|
|
41
|
+
// - iOS renders the shadow along the transparent contours of the image.
|
|
42
|
+
// - Android renders an underlay which extends to the inside of the bounds.
|
|
43
|
+
// - Web renders the shadow only on the outside of the bounds.
|
|
44
|
+
// To achieve a consistent appearance on all platforms, it is highly recommended
|
|
45
|
+
// to set a background-color on the Image when using shadows. This will ensure
|
|
46
|
+
// consistent rendering on all platforms and mitigate Androids drawing artefacts.
|
|
47
|
+
if (hasShadows) {
|
|
48
|
+
const processedColor = processColor(resolvedStyle.backgroundColor);
|
|
49
|
+
const bkColor = typeof processedColor === 'number' ? processedColor : 0;
|
|
50
|
+
const alpha = bkColor >> 24;
|
|
51
|
+
if (alpha !== -1 && alpha !== 255) {
|
|
52
|
+
// To silence this warning, set background-color to a fully transparent color
|
|
53
|
+
console.warn(`"expo-image" Shadows may not be rendered correctly for the transparent parts of images. Set "backgroundColor" to a non-transparent color when using a shadow.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return (React.createElement(NativeExpoImage, { ...props, source: resolvedSource, style: resolvedStyle, defaultSource: resolvedPlaceholder }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=ExpoImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImage.js","sourceRoot":"","sources":["../src/ExpoImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,KAAK,EACL,sBAAsB,EACtB,aAAa,EACb,UAAU,EACV,QAAQ,EACR,YAAY,GACb,MAAM,cAAc,CAAC;AAMtB,MAAM,eAAe,GAAG,sBAAsB,CAAuB,WAAW,CAAC,CAAC;AAElF,MAAM,eAAe,GAAG,aAAa,CAAC,eAAe,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,MAAM,EACN,KAAK,EACL,aAAa,EACb,sBAAsB,EACtB,GAAG,KAAK,EACG;IACX,MAAM,cAAc,GAAG,KAAK,CAAC,kBAAkB,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAClD,MAAM,mBAAmB,GAAG,KAAK,CAAC,kBAAkB,CAClD,aAAa,IAAI,sBAAsB,IAAI,EAAE,CAC9C,CAAC;IAEF,0DAA0D;IAC1D,IAAI,aAAa,IAAI,sBAAsB,EAAE;QAC3C,OAAO,CAAC,IAAI,CACV,0HAA0H,CAC3H,CAAC;KACH;IAED,iFAAiF;IACjF,sFAAsF;IACtF,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,sDAAsD;IACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE;QAClC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC;QACzC,aAAa,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,IAAI,KAAK,CAAC;QACnD,aAAa,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,IAAI,MAAM,CAAC;KACvD;IAED,kDAAkD;IAClD,iDAAiD;IACjD,6CAA6C;IAC7C,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE;QAC7B,OAAO,aAAa,CAAC,WAAW,CAAC;QACjC,OAAO,aAAa,CAAC,YAAY,CAAC;QAClC,OAAO,aAAa,CAAC,aAAa,CAAC;QACnC,OAAO,aAAa,CAAC,YAAY,CAAC;QAClC,UAAU,GAAG,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC;KACxC;SAAM;QACL,OAAO,aAAa,CAAC,SAAS,CAAC;QAC/B,UAAU,GAAG,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC;KAC1C;IAED,kEAAkE;IAClE,wEAAwE;IACxE,2EAA2E;IAC3E,8DAA8D;IAC9D,gFAAgF;IAChF,8EAA8E;IAC9E,iFAAiF;IACjF,IAAI,UAAU,EAAE;QACd,MAAM,cAAc,GAAG,YAAY,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;QACnE,MAAM,OAAO,GAAW,OAAO,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,OAAO,IAAI,EAAE,CAAC;QAC5B,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,KAAK,KAAK,GAAG,EAAE;YACjC,6EAA6E;YAC7E,OAAO,CAAC,IAAI,CACV,+JAA+J,CAChK,CAAC;SACH;KACF;IAED,OAAO,CACL,oBAAC,eAAe,OACV,KAAK,EACT,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,aAAa,EACpB,aAAa,EAAE,mBAAmB,GAClC,CACH,CAAC;AACJ,CAAC","sourcesContent":["import React from 'react';\nimport {\n Image,\n requireNativeComponent,\n NativeModules,\n StyleSheet,\n Platform,\n processColor,\n} from 'react-native';\n\nimport { ImageProps } from './Image';\n\ntype NativeExpoImageProps = ImageProps;\n\nconst NativeExpoImage = requireNativeComponent<NativeExpoImageProps>('ExpoImage');\n\nconst ExpoImageModule = NativeModules.ExpoImageModule;\n\nexport { ExpoImageModule };\n\nexport default function ExpoImage({\n source,\n style,\n defaultSource,\n loadingIndicatorSource,\n ...props\n}: ImageProps) {\n const resolvedSource = Image.resolveAssetSource(source ?? {});\n const resolvedStyle = StyleSheet.flatten([style]);\n const resolvedPlaceholder = Image.resolveAssetSource(\n defaultSource ?? loadingIndicatorSource ?? {}\n );\n\n // If both are specified, we default to use default source\n if (defaultSource && loadingIndicatorSource) {\n console.warn(\n \"<Image> component can't have both defaultSource and loadingIndicatorSource at the same time. Defaulting to defaultSource\"\n );\n }\n\n // When possible, pass through the intrinsic size of the asset to the Yoga layout\n // system. While this is also possible in native code, doing it here is more efficient\n // as the yoga node gets initialized with the correct size from the start.\n // In native code, there is a separation between the layout (shadow) nodes and\n // actual views. Views that update the intrinsic content-size in Yoga trigger\n // additional layout passes, which we want to prevent.\n if (!Array.isArray(resolvedSource)) {\n const { width, height } = resolvedSource;\n resolvedStyle.width = resolvedStyle.width ?? width;\n resolvedStyle.height = resolvedStyle.height ?? height;\n }\n\n // Shadows behave different on iOS, Android & Web.\n // Android uses the `elevation` prop, whereas iOS\n // and web use the regular `shadow...` props.\n let hasShadows = false;\n if (Platform.OS === 'android') {\n delete resolvedStyle.shadowColor;\n delete resolvedStyle.shadowOffset;\n delete resolvedStyle.shadowOpacity;\n delete resolvedStyle.shadowRadius;\n hasShadows = !!resolvedStyle.elevation;\n } else {\n delete resolvedStyle.elevation;\n hasShadows = !!resolvedStyle.shadowColor;\n }\n\n // Shadows are rendered quite differently on iOS, Android and web.\n // - iOS renders the shadow along the transparent contours of the image.\n // - Android renders an underlay which extends to the inside of the bounds.\n // - Web renders the shadow only on the outside of the bounds.\n // To achieve a consistent appearance on all platforms, it is highly recommended\n // to set a background-color on the Image when using shadows. This will ensure\n // consistent rendering on all platforms and mitigate Androids drawing artefacts.\n if (hasShadows) {\n const processedColor = processColor(resolvedStyle.backgroundColor);\n const bkColor: number = typeof processedColor === 'number' ? processedColor : 0;\n const alpha = bkColor >> 24;\n if (alpha !== -1 && alpha !== 255) {\n // To silence this warning, set background-color to a fully transparent color\n console.warn(\n `\"expo-image\" Shadows may not be rendered correctly for the transparent parts of images. Set \"backgroundColor\" to a non-transparent color when using a shadow.`\n );\n }\n }\n\n return (\n <NativeExpoImage\n {...props}\n source={resolvedSource}\n style={resolvedStyle}\n defaultSource={resolvedPlaceholder}\n />\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImage.web.d.ts","sourceRoot":"","sources":["../src/ExpoImage.web.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,eAKjE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Image } from 'react-native';
|
|
3
|
+
export default function ExpoImage({ source, ...props }) {
|
|
4
|
+
const resolvedSource = source ?? {};
|
|
5
|
+
// @ts-expect-error - expo-image is being reworked so these types should be revisited
|
|
6
|
+
return React.createElement(Image, { ...props, source: resolvedSource });
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=ExpoImage.web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoImage.web.js","sourceRoot":"","sources":["../src/ExpoImage.web.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAIrC,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,KAAK,EAAc;IAChE,MAAM,cAAc,GAAG,MAAM,IAAI,EAAE,CAAC;IAEpC,qFAAqF;IACrF,OAAO,oBAAC,KAAK,OAAK,KAAK,EAAE,MAAM,EAAE,cAAc,GAAI,CAAC;AACtD,CAAC","sourcesContent":["import React from 'react';\nimport { Image } from 'react-native';\n\nimport { ImageProps } from './Image';\n\nexport default function ExpoImage({ source, ...props }: ImageProps) {\n const resolvedSource = source ?? {};\n\n // @ts-expect-error - expo-image is being reworked so these types should be revisited\n return <Image {...props} source={resolvedSource} />;\n}\n"]}
|
package/build/Image.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AccessibilityProps, ImageResizeMode, ImageSourcePropType, ImageStyle as RNImageStyle, NativeSyntheticEvent, StyleProp } from 'react-native';
|
|
3
|
+
import { ImageErrorEventData, ImageLoadEventData, ImageLoadProgressEventData } from './Image.types';
|
|
4
|
+
interface ImageStyle extends RNImageStyle {
|
|
5
|
+
elevation?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ImageProps extends AccessibilityProps {
|
|
8
|
+
source?: ImageSourcePropType | null;
|
|
9
|
+
style?: StyleProp<ImageStyle>;
|
|
10
|
+
defaultSource?: ImageSourcePropType | null;
|
|
11
|
+
loadingIndicatorSource?: ImageSourcePropType | null;
|
|
12
|
+
resizeMode?: ImageResizeMode;
|
|
13
|
+
/**
|
|
14
|
+
* @Android only
|
|
15
|
+
*/
|
|
16
|
+
blurRadius?: number;
|
|
17
|
+
fadeDuration?: number;
|
|
18
|
+
onLoadStart?: () => void;
|
|
19
|
+
onProgress?: (event: NativeSyntheticEvent<ImageLoadProgressEventData>) => void;
|
|
20
|
+
onLoad?: (event: NativeSyntheticEvent<ImageLoadEventData>) => void;
|
|
21
|
+
onError?: (error: NativeSyntheticEvent<ImageErrorEventData>) => void;
|
|
22
|
+
onLoadEnd?: () => void;
|
|
23
|
+
}
|
|
24
|
+
interface ImageState {
|
|
25
|
+
onLoad: ImageProps['onLoad'];
|
|
26
|
+
onError: ImageProps['onError'];
|
|
27
|
+
}
|
|
28
|
+
export default class Image extends React.Component<ImageProps, ImageState> {
|
|
29
|
+
static getDerivedStateFromProps(props: ImageProps): {
|
|
30
|
+
onLoad: ((event: NativeSyntheticEvent<ImageLoadEventData>) => void) | undefined;
|
|
31
|
+
onError: ((error: NativeSyntheticEvent<ImageErrorEventData>) => void) | undefined;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* **Available on @Android only.** Caching the image that can be later used in ImageView
|
|
35
|
+
* @return an empty promise.
|
|
36
|
+
*/
|
|
37
|
+
static prefetch(url: string): Promise<void>;
|
|
38
|
+
state: {
|
|
39
|
+
onLoad: undefined;
|
|
40
|
+
onError: undefined;
|
|
41
|
+
};
|
|
42
|
+
render(): JSX.Element;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=Image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../src/Image.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,IAAI,YAAY,EAC1B,oBAAoB,EACpB,SAAS,EAEV,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAIpG,UAAU,UAAW,SAAQ,YAAY;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAW,SAAQ,kBAAkB;IAMpD,MAAM,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAC9B,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,sBAAsB,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACpD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,KAAK,IAAI,CAAC;IAC/E,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC;IACrE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,UAAU,UAAU;IAClB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;CAChC;AAED,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,KAAK,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC;IACxE,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,UAAU;;;;IAqBjD;;;OAGG;WACU,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,KAAK;;;MAGH;IAEF,MAAM;CAgBP"}
|
package/build/Image.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Platform, UnavailabilityError } from 'expo-modules-core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { StyleSheet, } from 'react-native';
|
|
4
|
+
import ExpoImage, { ExpoImageModule } from './ExpoImage';
|
|
5
|
+
const DEFAULT_RESIZE_MODE = 'cover';
|
|
6
|
+
export default class Image extends React.Component {
|
|
7
|
+
static getDerivedStateFromProps(props) {
|
|
8
|
+
return {
|
|
9
|
+
onLoad: props.onLoadEnd
|
|
10
|
+
? (e) => {
|
|
11
|
+
if (props.onLoad) {
|
|
12
|
+
props.onLoad(e);
|
|
13
|
+
}
|
|
14
|
+
props.onLoadEnd();
|
|
15
|
+
}
|
|
16
|
+
: props.onLoad,
|
|
17
|
+
onError: props.onLoadEnd
|
|
18
|
+
? (e) => {
|
|
19
|
+
if (props.onError) {
|
|
20
|
+
props.onError(e);
|
|
21
|
+
}
|
|
22
|
+
props.onLoadEnd();
|
|
23
|
+
}
|
|
24
|
+
: props.onError,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* **Available on @Android only.** Caching the image that can be later used in ImageView
|
|
29
|
+
* @return an empty promise.
|
|
30
|
+
*/
|
|
31
|
+
static async prefetch(url) {
|
|
32
|
+
if (Platform.OS !== 'android') {
|
|
33
|
+
throw new UnavailabilityError('Image', 'prefetch');
|
|
34
|
+
}
|
|
35
|
+
return await ExpoImageModule.prefetch(url);
|
|
36
|
+
}
|
|
37
|
+
state = {
|
|
38
|
+
onLoad: undefined,
|
|
39
|
+
onError: undefined,
|
|
40
|
+
};
|
|
41
|
+
render() {
|
|
42
|
+
const { style, resizeMode: resizeModeProp, ...restProps } = this.props;
|
|
43
|
+
const { resizeMode: resizeModeStyle, ...restStyle } = StyleSheet.flatten([style]) || {};
|
|
44
|
+
const resizeMode = resizeModeProp ?? resizeModeStyle ?? DEFAULT_RESIZE_MODE;
|
|
45
|
+
return (React.createElement(ExpoImage, { ...restProps, style: restStyle, resizeMode: resizeMode, onLoad: this.state.onLoad, onError: this.state.onError }));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=Image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Image.js","sourceRoot":"","sources":["../src/Image.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAOL,UAAU,GACX,MAAM,cAAc,CAAC;AAEtB,OAAO,SAAS,EAAE,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzD,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAmCpC,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,KAAK,CAAC,SAAiC;IACxE,MAAM,CAAC,wBAAwB,CAAC,KAAiB;QAC/C,OAAO;YACL,MAAM,EAAE,KAAK,CAAC,SAAS;gBACrB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACJ,IAAI,KAAK,CAAC,MAAM,EAAE;wBAChB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;qBACjB;oBACD,KAAK,CAAC,SAAU,EAAE,CAAC;gBACrB,CAAC;gBACH,CAAC,CAAC,KAAK,CAAC,MAAM;YAChB,OAAO,EAAE,KAAK,CAAC,SAAS;gBACtB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACJ,IAAI,KAAK,CAAC,OAAO,EAAE;wBACjB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;qBAClB;oBACD,KAAK,CAAC,SAAU,EAAE,CAAC;gBACrB,CAAC;gBACH,CAAC,CAAC,KAAK,CAAC,OAAO;SAClB,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAW;QAC/B,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE;YAC7B,MAAM,IAAI,mBAAmB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;SACpD;QACD,OAAO,MAAM,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,GAAG;QACN,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS;KACnB,CAAC;IAEF,MAAM;QACJ,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAEvE,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QACxF,MAAM,UAAU,GAAG,cAAc,IAAI,eAAe,IAAI,mBAAmB,CAAC;QAE5E,OAAO,CACL,oBAAC,SAAS,OACJ,SAAS,EACb,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EACzB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAC3B,CACH,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { Platform, UnavailabilityError } from 'expo-modules-core';\nimport React from 'react';\nimport {\n AccessibilityProps,\n ImageResizeMode,\n ImageSourcePropType,\n ImageStyle as RNImageStyle,\n NativeSyntheticEvent,\n StyleProp,\n StyleSheet,\n} from 'react-native';\n\nimport ExpoImage, { ExpoImageModule } from './ExpoImage';\nimport { ImageErrorEventData, ImageLoadEventData, ImageLoadProgressEventData } from './Image.types';\n\nconst DEFAULT_RESIZE_MODE = 'cover';\n\ninterface ImageStyle extends RNImageStyle {\n elevation?: number;\n}\n\nexport interface ImageProps extends AccessibilityProps {\n // On one hand we want to pass resolved source to native module.\n // On the other hand, react-native-web doesn't expose a resolveAssetSource\n // function, so we can't use it there. So we pass the unresolved source\n // to \"native components\" and they decide whether to resolve the value\n // or not.\n source?: ImageSourcePropType | null;\n style?: StyleProp<ImageStyle>;\n defaultSource?: ImageSourcePropType | null;\n loadingIndicatorSource?: ImageSourcePropType | null;\n resizeMode?: ImageResizeMode;\n /**\n * @Android only\n */\n blurRadius?: number;\n fadeDuration?: number;\n\n onLoadStart?: () => void;\n onProgress?: (event: NativeSyntheticEvent<ImageLoadProgressEventData>) => void;\n onLoad?: (event: NativeSyntheticEvent<ImageLoadEventData>) => void;\n onError?: (error: NativeSyntheticEvent<ImageErrorEventData>) => void;\n onLoadEnd?: () => void;\n}\n\ninterface ImageState {\n onLoad: ImageProps['onLoad'];\n onError: ImageProps['onError'];\n}\n\nexport default class Image extends React.Component<ImageProps, ImageState> {\n static getDerivedStateFromProps(props: ImageProps) {\n return {\n onLoad: props.onLoadEnd\n ? (e) => {\n if (props.onLoad) {\n props.onLoad(e);\n }\n props.onLoadEnd!();\n }\n : props.onLoad,\n onError: props.onLoadEnd\n ? (e) => {\n if (props.onError) {\n props.onError(e);\n }\n props.onLoadEnd!();\n }\n : props.onError,\n };\n }\n\n /**\n * **Available on @Android only.** Caching the image that can be later used in ImageView\n * @return an empty promise.\n */\n static async prefetch(url: string): Promise<void> {\n if (Platform.OS !== 'android') {\n throw new UnavailabilityError('Image', 'prefetch');\n }\n return await ExpoImageModule.prefetch(url);\n }\n\n state = {\n onLoad: undefined,\n onError: undefined,\n };\n\n render() {\n const { style, resizeMode: resizeModeProp, ...restProps } = this.props;\n\n const { resizeMode: resizeModeStyle, ...restStyle } = StyleSheet.flatten([style]) || {};\n const resizeMode = resizeModeProp ?? resizeModeStyle ?? DEFAULT_RESIZE_MODE;\n\n return (\n <ExpoImage\n {...restProps}\n style={restStyle}\n resizeMode={resizeMode}\n onLoad={this.state.onLoad}\n onError={this.state.onError}\n />\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare enum ImageCacheType {
|
|
2
|
+
UNKNOWN = 0,
|
|
3
|
+
NONE = 1,
|
|
4
|
+
DISK = 2,
|
|
5
|
+
MEMORY = 3
|
|
6
|
+
}
|
|
7
|
+
export interface ImageLoadEventData {
|
|
8
|
+
cacheType?: ImageCacheType;
|
|
9
|
+
source: {
|
|
10
|
+
url: string;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
mediaType?: string | null;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface ImageLoadProgressEventData {
|
|
17
|
+
loaded: number;
|
|
18
|
+
total: number;
|
|
19
|
+
}
|
|
20
|
+
interface AndroidThrowable {
|
|
21
|
+
class: string;
|
|
22
|
+
cause: AndroidThrowable | null;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
interface AndroidGlideException extends AndroidThrowable {
|
|
26
|
+
origin: AndroidThrowable | null;
|
|
27
|
+
causes: AndroidThrowable[] | null;
|
|
28
|
+
}
|
|
29
|
+
export interface ImageErrorEventData {
|
|
30
|
+
error: string;
|
|
31
|
+
ios?: {
|
|
32
|
+
code: number;
|
|
33
|
+
domain: string;
|
|
34
|
+
description: string;
|
|
35
|
+
helpAnchor: string | null;
|
|
36
|
+
failureReason: string | null;
|
|
37
|
+
recoverySuggestion: string | null;
|
|
38
|
+
};
|
|
39
|
+
android?: AndroidGlideException | null;
|
|
40
|
+
}
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=Image.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Image.types.d.ts","sourceRoot":"","sources":["../src/Image.types.ts"],"names":[],"mappings":"AAAA,oBAAY,cAAc;IACxB,OAAO,IAAI;IACX,IAAI,IAAI;IACR,IAAI,IAAI;IACR,MAAM,IAAI;CACX;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC;CACH;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,gBAAgB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,qBAAsB,SAAQ,gBAAgB;IACtD,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;IACF,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export var ImageCacheType;
|
|
2
|
+
(function (ImageCacheType) {
|
|
3
|
+
ImageCacheType[ImageCacheType["UNKNOWN"] = 0] = "UNKNOWN";
|
|
4
|
+
ImageCacheType[ImageCacheType["NONE"] = 1] = "NONE";
|
|
5
|
+
ImageCacheType[ImageCacheType["DISK"] = 2] = "DISK";
|
|
6
|
+
ImageCacheType[ImageCacheType["MEMORY"] = 3] = "MEMORY";
|
|
7
|
+
})(ImageCacheType || (ImageCacheType = {}));
|
|
8
|
+
//# sourceMappingURL=Image.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Image.types.js","sourceRoot":"","sources":["../src/Image.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,cAKX;AALD,WAAY,cAAc;IACxB,yDAAW,CAAA;IACX,mDAAQ,CAAA;IACR,mDAAQ,CAAA;IACR,uDAAU,CAAA;AACZ,CAAC,EALW,cAAc,KAAd,cAAc,QAKzB","sourcesContent":["export enum ImageCacheType {\n UNKNOWN = 0,\n NONE = 1,\n DISK = 2,\n MEMORY = 3,\n}\n\nexport interface ImageLoadEventData {\n cacheType?: ImageCacheType;\n source: {\n url: string;\n width: number;\n height: number;\n mediaType?: string | null;\n };\n}\n\nexport interface ImageLoadProgressEventData {\n loaded: number;\n total: number;\n}\n\ninterface AndroidThrowable {\n class: string;\n cause: AndroidThrowable | null;\n message: string;\n}\n\ninterface AndroidGlideException extends AndroidThrowable {\n origin: AndroidThrowable | null;\n causes: AndroidThrowable[] | null;\n}\n\nexport interface ImageErrorEventData {\n error: string;\n ios?: {\n code: number;\n domain: string;\n description: string;\n helpAnchor: string | null;\n failureReason: string | null;\n recoverySuggestion: string | null;\n };\n android?: AndroidGlideException | null;\n}\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,eAAe,KAAK,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,eAAe,KAAK,CAAC","sourcesContent":["import Image from './Image';\nexport * from './Image.types';\nexport default Image;\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-image",
|
|
3
3
|
"title": "Expo Image",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "A cross-platform, performant image component for React Native and Expo with Web support",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
@@ -26,8 +26,12 @@
|
|
|
26
26
|
],
|
|
27
27
|
"author": "650 Industries, Inc.",
|
|
28
28
|
"license": "MIT",
|
|
29
|
+
"dependencies": {},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"expo-module-scripts": "^2.0.0"
|
|
31
32
|
},
|
|
32
|
-
"
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"expo": "*"
|
|
35
|
+
},
|
|
36
|
+
"gitHead": "2b35d9008fef42cb53473331c37693ca12e9bf12"
|
|
33
37
|
}
|