diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 57fb6b3a..8f0a0767 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -64,5 +64,5 @@ jobs:
working-directory: example/windows
- name: Build
run: |
- MSBuild -t:Rebuild -p:Configuration=Release -p:Platform=x64 ReactTestApp.sln
+ MSBuild -t:Rebuild -p:Configuration=Release -p:Platform=x64 -p:BundleEntryFile=index.ts ReactTestApp.sln
working-directory: example/windows
diff --git a/example/App.js b/example/App.js
deleted file mode 100644
index b2fe194f..00000000
--- a/example/App.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @format
- * @flow
- */
-
-import React, {Component} from 'react';
-import {
- StyleSheet,
- SafeAreaView,
- Text,
- TouchableOpacity,
- View,
- Keyboard,
- Button,
-} from 'react-native';
-
-import GetSetClear from './examples/GetSetClear';
-import MergeItem from './examples/MergeItem';
-import BasicExample from './examples/Basic';
-
-const TESTS = {
- GetSetClear: {
- title: 'Simple Get/Set value',
- testId: 'get-set-clear',
- description: 'Store and retrieve persisted data',
- render() {
- return ;
- },
- },
- MergeItem: {
- title: 'Merge item',
- testId: 'merge-item',
- description: 'Merge object with already stored data',
- render() {
- return ;
- },
- },
- Basic: {
- title: 'Basic',
- testId: 'basic',
- description: 'Basic functionality test',
- render() {
- return ;
- },
- },
-};
-
-type Props = {};
-type State = {restarting: boolean, currentTest: Object};
-
-export default class App extends Component {
- state = {
- restarting: false,
- currentTest: TESTS.GetSetClear,
- };
-
- _simulateRestart = () => {
- this.setState({restarting: true}, () => this.setState({restarting: false}));
- };
-
- _changeTest = (testName) => {
- this.setState({currentTest: TESTS[testName]});
- };
-
- render() {
- const {restarting, currentTest} = this.state;
- return (
-
- Keyboard.dismiss()}
- testID="closeKeyboard"
- />
-
-
- Simulate Restart
-
-
-
-
-
- {restarting ? null : (
-
- {currentTest.title}
-
- {currentTest.description}
-
-
- {currentTest.render()}
-
-
- )}
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#F5FCFF',
- padding: 8,
- },
- exampleContainer: {
- padding: 4,
- backgroundColor: '#FFF',
- borderColor: '#EEE',
- borderTopWidth: 1,
- borderBottomWidth: 1,
- flex: 1,
- },
- exampleTitle: {
- fontSize: 18,
- },
- exampleDescription: {
- color: '#333333',
- marginBottom: 16,
- },
- exampleInnerContainer: {
- borderColor: '#EEE',
- borderTopWidth: 1,
- paddingTop: 10,
- flex: 1,
- },
- restartButton: {
- padding: 6,
- borderRadius: 5,
- backgroundColor: '#F3F3F3',
- alignItems: 'center',
- justifyContent: 'center',
- alignSelf: 'flex-end',
- },
- closeKeyboardView: {
- width: 5,
- height: 5,
- },
- testPickerContainer: {
- flexDirection: 'row',
- flexWrap: 'wrap',
- },
-});
diff --git a/example/App.tsx b/example/App.tsx
new file mode 100644
index 00000000..84a3c060
--- /dev/null
+++ b/example/App.tsx
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React, { useCallback, useState } from 'react';
+import {
+ Button,
+ Keyboard,
+ SafeAreaView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View
+} from 'react-native';
+import BasicExample from './examples/Basic';
+import GetSetClear from './examples/GetSetClear';
+import MergeItem from './examples/MergeItem';
+
+const TESTS = {
+ GetSetClear: {
+ title: 'Simple Get/Set value',
+ testId: 'get-set-clear',
+ description: 'Store and retrieve persisted data',
+ render() {
+ return ;
+ },
+ },
+ MergeItem: {
+ title: 'Merge item',
+ testId: 'merge-item',
+ description: 'Merge object with already stored data',
+ render() {
+ return ;
+ },
+ },
+ Basic: {
+ title: 'Basic',
+ testId: 'basic',
+ description: 'Basic functionality test',
+ render() {
+ return ;
+ },
+ },
+};
+
+export default function App(): JSX.Element {
+ const [iteration, setIteration] = useState(0);
+ const [currentTest, setCurrentTest] = useState(TESTS.GetSetClear);
+
+ const dismissKeyboard = useCallback(() => Keyboard.dismiss(), []);
+ const simulateRestart = useCallback(() => setIteration(iteration + 1), [iteration]);
+ const testBasic = useCallback(() => setCurrentTest(TESTS.Basic), []);
+ const testGetSetClear = useCallback(() => setCurrentTest(TESTS.GetSetClear), []);
+ const testMergeItem = useCallback(() => setCurrentTest(TESTS.MergeItem), []);
+
+ return (
+
+
+
+
+ Simulate Restart
+
+
+
+
+
+
+
+
+
+ {currentTest.title}
+
+ {currentTest.description}
+
+
+ {currentTest.render()}
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#F5FCFF',
+ padding: 8,
+ },
+ exampleContainer: {
+ padding: 4,
+ backgroundColor: '#FFF',
+ borderColor: '#EEE',
+ borderTopWidth: 1,
+ borderBottomWidth: 1,
+ flex: 1,
+ },
+ exampleTitle: {
+ fontSize: 18,
+ },
+ exampleDescription: {
+ color: '#333333',
+ marginBottom: 16,
+ },
+ exampleInnerContainer: {
+ borderColor: '#EEE',
+ borderTopWidth: 1,
+ paddingTop: 10,
+ flex: 1,
+ },
+ restartButton: {
+ padding: 6,
+ borderRadius: 5,
+ backgroundColor: '#F3F3F3',
+ alignItems: 'center',
+ justifyContent: 'center',
+ alignSelf: 'flex-end',
+ },
+ closeKeyboardView: {
+ width: 5,
+ height: 5,
+ },
+ testPickerContainer: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ },
+});
diff --git a/example/examples/Basic.js b/example/examples/Basic.tsx
similarity index 87%
rename from example/examples/Basic.js
rename to example/examples/Basic.tsx
index 4dc2375e..93e119ca 100644
--- a/example/examples/Basic.js
+++ b/example/examples/Basic.tsx
@@ -1,7 +1,13 @@
-import React from 'react';
-import {View, Text, Button, StyleSheet, ScrollView, TextInput} from 'react-native';
+// @ts-ignore
import AsyncStorage from '@react-native-async-storage/async-storage';
+import React from 'react';
+import { Button, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
+type DataType = {
+ deeper?: DataType;
+ nested?: DataType;
+ [key: string]: string | DataType | undefined;
+};
const mergeInitialValue = {
initial: 'keep',
@@ -16,11 +22,15 @@ const mergeInitialValue = {
},
};
+function hasMessage(e: unknown): e is { "message": string; } {
+ return Boolean(typeof e === 'object' && e && 'message' in e);
+}
+
function NextExample() {
const [keys, setKeys] = React.useState([]);
- const [error, setError] = React.useState(null);
- const [inputKey, setInputKey] = React.useState();
- const [inputValue, setInputValue] = React.useState();
+ const [error, setError] = React.useState('');
+ const [inputKey, setInputKey] = React.useState('');
+ const [inputValue, setInputValue] = React.useState('');
const [value, setValue] = React.useState();
const [mergedValue, setMergedValue] = React.useState();
const [overrideValue, setOverrideValue] = React.useState({
@@ -30,13 +40,17 @@ function NextExample() {
});
- function runWithCatch(block) {
+ function runWithCatch(block: () => Promise) {
return async () => {
try {
- setError(null);
+ setError('');
await block();
} catch (e) {
- setError('Caught: ' + e.message || e);
+ if (hasMessage(e)) {
+ setError('Caught error: ' + (e.message || e));
+ } else {
+ setError('Unknown error: ' + e);
+ }
}
};
}
@@ -90,9 +104,9 @@ function NextExample() {
const {override1, override2, override3} = overrideValue;
// leave out empty inputs
- const toMerge = {};
+ const toMerge: DataType = {};
if (override1) {
- toMerge.override1 = override1;
+ toMerge['override1'] = override1;
}
if (override2) {
toMerge.nested = {
@@ -186,7 +200,6 @@ function NextExample() {
;
}
-
const styles = StyleSheet.create({
example: {
paddingBottom: 24,
diff --git a/example/examples/GetSetClear.js b/example/examples/GetSetClear.tsx
similarity index 92%
rename from example/examples/GetSetClear.js
rename to example/examples/GetSetClear.tsx
index 6cc1853c..3b6ba6ed 100644
--- a/example/examples/GetSetClear.js
+++ b/example/examples/GetSetClear.tsx
@@ -3,22 +3,19 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- *
- * @format
- * @flow
*/
-import React from 'react';
-import {StyleSheet, Text, View, Button} from 'react-native';
-
+// @ts-ignore
import AsyncStorage from '@react-native-async-storage/async-storage';
+import React from 'react';
+import { Button, StyleSheet, Text, View } from 'react-native';
export default function GetSet() {
const [storedNumber, setStoredNumber] = React.useState('');
const [needsRestart, setNeedsRestart] = React.useState(false);
React.useEffect(() => {
- AsyncStorage.getItem(STORAGE_KEY).then((value) => {
+ AsyncStorage.getItem(STORAGE_KEY).then((value: string) => {
if (value) {
setStoredNumber(value);
}
diff --git a/example/examples/MergeItem.js b/example/examples/MergeItem.js
deleted file mode 100644
index 03004799..00000000
--- a/example/examples/MergeItem.js
+++ /dev/null
@@ -1,268 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @format
- * @flow
- */
-
-import React, {Component} from 'react';
-import {
- Button,
- NativeModules,
- StyleSheet,
- Text,
- TextInput,
- View,
-} from 'react-native';
-
-import AsyncStorage from '@react-native-async-storage/async-storage';
-
-const KEY = '@@KEY';
-
-type Props = {};
-type State = {
- needRestart: boolean,
- name: string,
- age: string,
- traits: {
- trait1: string,
- trait2: string,
- },
-};
-
-const INPUTS = [
- {
- title: 'Name',
- stateFragment: 'name',
- testId: 'testInput-name',
- },
- {
- title: 'Age',
- stateFragment: 'age',
- inputType: 'number-pad',
- testId: 'testInput-age',
- },
- {
- title: 'Eyes color',
- stateFragment: 'trait1',
- testId: 'testInput-eyes',
- },
- {
- title: 'Shoe size',
- stateFragment: 'trait2',
- inputType: 'number-pad',
- testId: 'testInput-shoe',
- },
-];
-
-export default class Merge extends Component {
- state = {
- needRestart: false,
- name: '',
- age: '',
- traits: {
- trait1: '',
- trait2: '',
- },
- };
-
- mergeItem = async () => {
- const {
- name,
- age,
- traits: {trait1, trait2},
- } = this.state;
-
- const obj = {
- name,
- age,
- traits: {
- trait1,
- trait2,
- },
- };
-
- try {
- await AsyncStorage.mergeItem(KEY, JSON.stringify(obj));
- } catch (e) {
- console.warn(e);
- }
-
- this.setState({needRestart: true});
- };
-
- saveItem = async () => {
- const {
- name,
- age,
- traits: {trait1, trait2},
- } = this.state;
-
- const obj = {
- name,
- age,
- traits: {
- trait1,
- trait2,
- },
- };
-
- try {
- await AsyncStorage.setItem(KEY, JSON.stringify(obj));
- } catch (e) {
- console.warn(e);
- }
-
- this.setState({needRestart: true});
- };
-
- restoreItem = async () => {
- let storedItem = {};
-
- try {
- const saved = await AsyncStorage.getItem(KEY);
- storedItem = JSON.parse(saved || '{"traits": {}}');
- } catch (e) {
- console.warn(e);
- }
-
- const {traits} = storedItem || {};
-
- this.setState({
- name: storedItem.name || '',
- age: storedItem.age || '',
- traits: {
- trait1: traits.trait1 || '',
- trait2: traits.trait2 || '',
- },
- });
- };
-
- render() {
- const {needRestart, name, age, traits} = this.state;
- const {trait1, trait2} = traits;
- return (
-
-
- {`${name} is ${age}, has ${trait1} eyes and shoe size of ${trait2}.`}
-
-
- {INPUTS.map((input) => {
- const isTraitsPart = input.stateFragment.includes('trait');
-
- const value = isTraitsPart
- ? // $FlowFixMe
- traits[input.stateFragment]
- : // $FlowFixMe
- this.state[input.stateFragment];
-
- const onChangeHandler = (text) => {
- isTraitsPart
- ? this.setState(({traits: currentTraits}) => ({
- // $FlowFixMe
- traits: {
- // $FlowFixMe
- ...currentTraits,
- // $FlowFixMe
- [input.stateFragment]: text,
- },
- }))
- : this.setState((state) =>
- // $FlowFixMe
- ({
- // $FlowFixMe
- ...state,
- // $FlowFixMe
- [input.stateFragment]: text,
- }),
- );
- };
-
- return (
-
- {input.title}:
-
-
- );
- })}
-
-
-
-
-
-
-
-
-
- NativeModules.AsyncStorageTestSupport.test_setDelegate(() => {})
- }
- />
-
- NativeModules.AsyncStorageTestSupport.test_unsetDelegate(() => {})
- }
- />
-
-
- {needRestart ? Hit restart to see effect : null}
-
- );
- }
-}
-
-const styles = StyleSheet.create({
- inputView: {
- borderColor: '#333',
- borderWidth: 0.5,
- borderStyle: 'solid',
- fontSize: 14,
- padding: 0,
- },
- bottomButtons: {
- justifyContent: 'space-around',
- marginTop: 20,
- flexDirection: 'row',
- },
- story: {
- fontSize: 18,
- color: '#222',
- },
-});
diff --git a/example/examples/MergeItem.tsx b/example/examples/MergeItem.tsx
new file mode 100644
index 00000000..f97170c6
--- /dev/null
+++ b/example/examples/MergeItem.tsx
@@ -0,0 +1,223 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+// @ts-ignore
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import React, {useCallback, useState} from 'react';
+import {
+ Button,
+ NativeModules,
+ StyleSheet,
+ Text,
+ TextInput,
+ View,
+} from 'react-native';
+
+type Personalia = {
+ age: string,
+ name: string,
+ traits: {
+ trait1: string,
+ trait2: string,
+ },
+};
+
+const KEY = '@@KEY';
+
+const INPUTS = [
+ {
+ title: 'Name',
+ stateFragment: 'name',
+ inputType: undefined,
+ testId: 'testInput-name',
+ },
+ {
+ title: 'Age',
+ stateFragment: 'age',
+ inputType: 'number-pad',
+ testId: 'testInput-age',
+ },
+ {
+ title: 'Eyes color',
+ stateFragment: 'trait1',
+ inputType: undefined,
+ testId: 'testInput-eyes',
+ },
+ {
+ title: 'Shoe size',
+ stateFragment: 'trait2',
+ inputType: 'number-pad',
+ testId: 'testInput-shoe',
+ },
+] as const;
+
+export default function Merge(): JSX.Element {
+ const [needRestart, setNeedRestart] = useState(false);
+ const [name, setName] = useState('');
+ const [age, setAge] = useState('');
+ const [traits, setTraits] = useState({trait1: '', trait2: ''});
+
+ const mergeItem = useCallback(async () => {
+ const obj = {name, age, traits};
+ try {
+ await AsyncStorage.mergeItem(KEY, JSON.stringify(obj));
+ } catch (e) {
+ console.warn(e);
+ }
+
+ setNeedRestart(true);
+ }, [name, age, traits]);
+
+ const saveItem = useCallback(async () => {
+ const obj = {name, age, traits};
+ try {
+ await AsyncStorage.setItem(KEY, JSON.stringify(obj));
+ } catch (e) {
+ console.warn(e);
+ }
+
+ setNeedRestart(true);
+ }, [name, age, traits]);
+
+ const restoreItem = useCallback(async () => {
+ let storedItem: Partial = {};
+
+ try {
+ const saved = await AsyncStorage.getItem(KEY);
+ storedItem = JSON.parse(saved || '{"traits": {}}');
+ } catch (e) {
+ console.warn(e);
+ }
+
+ const {name, age, traits} = storedItem || {};
+
+ setName(name || '');
+ setAge(age || '');
+ setTraits(traits || {trait1: '', trait2: ''});
+ }, []);
+
+ const {trait1, trait2} = traits;
+
+ return (
+
+
+ {`${name} is ${age}, has ${trait1} eyes and shoe size of ${trait2}.`}
+
+
+ {INPUTS.map(({title, stateFragment, inputType, testId}) => {
+ const value = (() => {
+ switch (stateFragment) {
+ case 'age':
+ return age;
+ case 'name':
+ return name;
+
+ case 'trait1':
+ case 'trait2':
+ return traits[stateFragment];
+ }
+ })();
+
+ const onChangeHandler = (() => {
+ switch (stateFragment) {
+ case 'age':
+ return setAge;
+ case 'name':
+ return setName;
+
+ case 'trait1':
+ case 'trait2':
+ return (text: string) =>
+ setTraits({
+ ...traits,
+ [stateFragment]: text,
+ });
+ }
+ })();
+
+ return (
+
+ {title}:
+
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+ NativeModules['AsyncStorageTestSupport'].test_setDelegate(() => {})
+ }
+ />
+
+ NativeModules['AsyncStorageTestSupport'].test_unsetDelegate(
+ () => {},
+ )
+ }
+ />
+
+
+ {needRestart ? Hit restart to see effect : null}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ inputView: {
+ borderColor: '#333',
+ borderWidth: 0.5,
+ borderStyle: 'solid',
+ fontSize: 14,
+ padding: 0,
+ },
+ bottomButtons: {
+ justifyContent: 'space-around',
+ marginTop: 20,
+ flexDirection: 'row',
+ },
+ story: {
+ fontSize: 18,
+ color: '#222',
+ },
+});
diff --git a/example/index.js b/example/index.ts
similarity index 95%
rename from example/index.js
rename to example/index.ts
index d457c8a0..109c3b75 100644
--- a/example/index.js
+++ b/example/index.ts
@@ -3,9 +3,6 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- *
- * @format
- * @flow
*/
import {AppRegistry, Platform} from 'react-native';
diff --git a/ios/RNCAsyncStorage.m b/ios/RNCAsyncStorage.m
index b25cf861..75c87471 100644
--- a/ios/RNCAsyncStorage.m
+++ b/ios/RNCAsyncStorage.m
@@ -316,7 +316,6 @@ static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
}
}
-
/**
* Determine which of RCTOldStorageDirectory or RCTExpoStorageDirectory needs to migrated.
* If both exist, we remove the least recently modified and return the most recently modified.
@@ -329,17 +328,18 @@ static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
NSString *oldStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTOldStorageDirectory);
NSString *expoStoragePath = RCTCreateStorageDirectoryPath_deprecated(RCTExpoStorageDirectory);
NSFileManager *fileManager = [NSFileManager defaultManager];
- BOOL oldStorageDirectoryExists = [fileManager fileExistsAtPath:oldStoragePath isDirectory:&isDir] && isDir;
- BOOL expoStorageDirectoryExists = [fileManager fileExistsAtPath:expoStoragePath isDirectory:&isDir] && isDir;
-
+ BOOL oldStorageDirectoryExists =
+ [fileManager fileExistsAtPath:oldStoragePath isDirectory:&isDir] && isDir;
+ BOOL expoStorageDirectoryExists =
+ [fileManager fileExistsAtPath:expoStoragePath isDirectory:&isDir] && isDir;
// Check if both the old storage directory and Expo storage directory exist
if (oldStorageDirectoryExists && expoStorageDirectoryExists) {
- // If the old storage has been modified more recently than Expo storage, then clear Expo storage.
- // Otherwise, clear the old storage.
+ // If the old storage has been modified more recently than Expo storage, then clear Expo
+ // storage. Otherwise, clear the old storage.
if ([RCTManifestModificationDate(RCTCreateManifestFilePath(oldStoragePath))
- compare:RCTManifestModificationDate(
- RCTCreateManifestFilePath(expoStoragePath))] == NSOrderedDescending) {
+ compare:RCTManifestModificationDate(RCTCreateManifestFilePath(expoStoragePath))] ==
+ NSOrderedDescending) {
RCTStorageDirectoryCleanupOld(expoStoragePath);
return oldStoragePath;
} else {
@@ -355,7 +355,6 @@ static void RCTStorageDirectoryMigrate(NSString *oldDirectoryPath,
}
}
-
/**
* This check is added to make sure that anyone coming from pre-1.2.2 does not lose cached data.
* Check that data is migrated from the old location to the new location
@@ -433,9 +432,7 @@ - (instancetype)init
// Migrate our deprecated path "Documents/.../RNCAsyncLocalStorage_V1" or
// "Documents/.../RCTAsyncLocalStorage" to "Documents/.../RCTAsyncLocalStorage_V1"
RCTStorageDirectoryMigrationCheck(
- oldStoragePath,
- RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory),
- YES);
+ oldStoragePath, RCTCreateStorageDirectoryPath_deprecated(RCTStorageDirectory), YES);
}
// Migrate what's in "Documents/.../RCTAsyncLocalStorage_V1" to
diff --git a/package.json b/package.json
index 7f67d773..bfc168e0 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
},
"scripts": {
"ci": "yarn --pure-lockfile --non-interactive --cache-folder .cache/yarn",
+ "format": "concurrently yarn:format:*",
"format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')",
"format:js": "prettier --write $(git ls-files '*.js' '*.json' '*.yml')",
"prepare": "bob build",
@@ -52,10 +53,11 @@
"build:e2e:macos": "scripts/macos_e2e.sh 'build'",
"bundle:android": "scripts/android_e2e.sh 'bundle'",
"bundle:ios": "scripts/ios_e2e.sh 'bundle'",
- "bundle:macos": "react-native bundle --entry-file index.js --platform macos --bundle-output example/index.macos.jsbundle --use-react-native-macos",
- "test": "yarn test:lint && yarn test:flow",
+ "bundle:macos": "react-native bundle --entry-file index.ts --platform macos --bundle-output example/index.macos.jsbundle --use-react-native-macos",
+ "test": "concurrently -n flow,lint,ts yarn:test:flow yarn:test:lint yarn:test:ts",
"test:flow": "flow check",
"test:lint": "eslint src/**/*.js example/**/*.js jest/*.js",
+ "test:ts": "tsc",
"test:e2e:android": "detox test -c android.emu.release --maxConcurrency 1",
"test:e2e:ios": "detox test -c ios.sim.release --maxConcurrency 1",
"test:e2e:macos": "scripts/macos_e2e.sh 'test'"
@@ -76,6 +78,9 @@
"@react-native-community/eslint-config": "^3.0.0",
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "9.0.0",
+ "@types/react": "^17.0.0",
+ "@types/react-native": "^0.64.0",
+ "concurrently": "^6.4.0",
"detox": "17.10.6",
"eslint": "^7.0.0",
"expo": "^38.0.10",
@@ -94,7 +99,8 @@
"react-native-web": "~0.12.0",
"react-native-windows": "^0.63.41",
"react-test-renderer": "16.13.1",
- "semantic-release": "^17.4.6"
+ "semantic-release": "^17.4.6",
+ "typescript": "^4.5.0"
},
"jest": {
"preset": "react-native",
diff --git a/scripts/android_e2e.sh b/scripts/android_e2e.sh
index b63d9eb2..797abff2 100755
--- a/scripts/android_e2e.sh
+++ b/scripts/android_e2e.sh
@@ -13,7 +13,7 @@ bundle_js() {
extraArgs="$@"
echo
echo "[Detox e2e] Bundling JS"
- react-native bundle --entry-file index.js --platform android --bundle-output example/index.android.jsbundle $extraArgs
+ react-native bundle --entry-file index.ts --platform android --bundle-output example/index.android.jsbundle $extraArgs
}
wait_for_emulator_to_boot() {
diff --git a/scripts/ios_e2e.sh b/scripts/ios_e2e.sh
index d189a39f..59ca4d33 100755
--- a/scripts/ios_e2e.sh
+++ b/scripts/ios_e2e.sh
@@ -1,7 +1,7 @@
#!/bin/bash
RESOURCE_DIR="$PWD/example/ios/build/Build/Products/Release-iphonesimulator/ReactTestApp.app"
-ENTRY_FILE="example/index.js"
+ENTRY_FILE="example/index.ts"
BUNDLE_FILE="$RESOURCE_DIR/main.jsbundle"
EXTRA_PACKAGER_ARGS="--entry-file=$ENTRY_FILE"
SIMULATOR_NAME="iPhone 11"
@@ -53,7 +53,7 @@ bundle_js() {
extraArgs="$@"
echo
echo "[Detox e2e] Bundling JS"
- react-native bundle --entry-file index.js --platform ios --bundle-output example/index.ios.jsbundle $extraArgs
+ react-native bundle --entry-file index.ts --platform ios --bundle-output example/index.ios.jsbundle $extraArgs
}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..477afb74
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitOverride": true,
+ "noImplicitReturns": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "strict": true,
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "noEmit": true,
+ "allowJs": true,
+ "checkJs": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "jsx": "react",
+ "target": "ESNext",
+ "skipLibCheck": true
+ },
+ "include": [
+ "example/*.ts",
+ "example/*.tsx",
+ "example/examples/**/*.tsx"
+ ]
+}
diff --git a/yarn.lock b/yarn.lock
index be7e33a2..d42d5fbb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2511,16 +2511,42 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3"
integrity sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==
+"@types/prop-types@*":
+ version "15.7.4"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
+ integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
+
"@types/q@^1.5.1":
version "1.5.5"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
+"@types/react-native@^0.64.0":
+ version "0.64.19"
+ resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.19.tgz#2b888c082ad293fa0fa6ae34c5e9457cfb38e50a"
+ integrity sha512-bT62QhaPvOKFGmlfURIC98ILjUDoIFrc2Bn5EzL3qciZrT91vHwkxFOkuEyQJy+DWaHCa2z3IrtIEJywkGu/Bg==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*", "@types/react@^17.0.0":
+ version "17.0.37"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959"
+ integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/retry@^0.12.0":
version "0.12.1"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
@@ -4539,6 +4565,20 @@ concat-stream@^1.5.0, concat-stream@^1.6.0:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+concurrently@^6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.4.0.tgz#5387ee86be435a0eb51c292ade8a00acf479f170"
+ integrity sha512-HZ3D0RTQMH3oS4gvtYj1P+NBc6PzE2McEra6yEFcQKrUQ9HvtTGU4Dbne083F034p+LRb7kWU0tPRNvSGs1UCQ==
+ dependencies:
+ chalk "^4.1.0"
+ date-fns "^2.16.1"
+ lodash "^4.17.21"
+ rxjs "^6.6.3"
+ spawn-command "^0.0.2-1"
+ supports-color "^8.1.0"
+ tree-kill "^1.2.2"
+ yargs "^16.2.0"
+
connect@^3.6.5:
version "3.7.0"
resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8"
@@ -5004,6 +5044,11 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
+csstype@^3.0.2:
+ version "3.0.10"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
+ integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
+
cyclist@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@@ -5025,6 +5070,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
+date-fns@^2.16.1:
+ version "2.27.0"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.27.0.tgz#e1ff3c3ddbbab8a2eaadbb6106be2929a5a2d92b"
+ integrity sha512-sj+J0Mo2p2X1e306MHq282WS4/A8Pz/95GIFcsPNMPMZVI3EUrAdSv90al1k+p74WGLCruMXk23bfEDZa71X9Q==
+
dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@@ -12434,6 +12484,13 @@ rx-lite@*, rx-lite@^4.0.8:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
+rxjs@^6.6.3:
+ version "6.6.7"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
+ integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
+ dependencies:
+ tslib "^1.9.0"
+
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -12923,6 +12980,11 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
+spawn-command@^0.0.2-1:
+ version "0.0.2-1"
+ resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
+ integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=
+
spawn-error-forwarder@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz#1afd94738e999b0346d7b9fc373be55e07577029"
@@ -13327,6 +13389,13 @@ supports-color@^7.0.0, supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
+supports-color@^8.1.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
supports-hyperlinks@^2.0.0, supports-hyperlinks@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
@@ -13674,6 +13743,11 @@ traverse@~0.6.6:
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=
+tree-kill@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
+ integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
+
treeverse@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f"
@@ -13701,7 +13775,7 @@ ts-pnp@^1.1.6:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
-tslib@^1.8.1:
+tslib@^1.8.1, tslib@^1.9.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
@@ -13806,6 +13880,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+typescript@^4.5.0:
+ version "4.5.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998"
+ integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==
+
ua-parser-js@^0.7.18:
version "0.7.28"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31"