Skip to content

Commit

Permalink
♻️ Move library to TypeScript (#467)
Browse files Browse the repository at this point in the history
* ➕ Install typescript dependencies
* 📦️ Define `@types/react-native` as an optional peerDependency
* Add 🔧 TypeScript configuration file
* ♻️ Move `src/index` to TypeScript
* ♻️ Move `src/ErrorBoundary/index` to TypeScript
* ♻️ Move `src/ErrorBoundary/FallbackComponent` to TypeScript
* ♻️ Update `eslint` configuration to TypeScript
* ♻️ Move unit tests to TypeScript
* ♻️ Replace `flow-bin` with `tsc`
* ♻️ Update coverage file extensions
* ✨ Add `build` script
* 👷 Run `build` in `npm-publish` workflow
* 🔧 Add `types` and `react-native` fields to package.json
* 🔥 Remove manual declaration file
* ✨ Export `ErrorBoundaryProps` and `FallbackComponentProps`
* ➖ Remove `@babel/preset-react` dependency
  • Loading branch information
carloscuesta authored Jan 8, 2023
1 parent 70a3775 commit c88b6c7
Show file tree
Hide file tree
Showing 16 changed files with 414 additions and 135 deletions.
6 changes: 0 additions & 6 deletions .flowconfig

This file was deleted.

4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ jobs:
run: yarn
- name: Lint 🎨
run: yarn run lint
- name: Flow types 🏷
run: yarn run flow
- name: Type check 🏷
run: yarn run typecheck
- name: Tests ✅
run: yarn run test
- name: Upload coverage 📈
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ jobs:
node-version: '16'
- name: Install dependencies 📦
run: yarn
- name: Build 👷‍♂️
run: yarn run build
- name: Publish package to NPM 🚀
env:
NPM_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
*.log
coverage
lib
6 changes: 5 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
env: {
test: {
presets: ['module:metro-react-native-babel-preset']
}
}
};
45 changes: 31 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
"name": "react-native-error-boundary",
"version": "1.1.17",
"description": "A simple and reusable React-Native error boundary component",
"main": "src/index.js",
"main": "lib/index.js",
"files": [
"src",
"lib",
"README.md"
],
"react-native": "src/index.tsx",
"types": "lib/index.d.ts",
"scripts": {
"flow": "flow ./src",
"build": "tsc",
"typecheck": "tsc --noEmit",
"lint": "eslint ./src",
"test": "jest",
"coverage:upload": "codecov"
Expand All @@ -17,7 +21,10 @@
"preset": "react-native",
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.js"
"src/**/*.(ts|tsx)"
],
"modulePathIgnorePatterns": [
"<rootDir>/lib"
]
},
"repository": {
Expand All @@ -42,46 +49,57 @@
},
"homepage": "https://github.com/carloscuesta/react-native-error-boundary",
"peerDependencies": {
"@types/react-native": ">=0.57.7",
"react": ">=16.6.0",
"react-native": ">=0.57.7"
},
"peerDependenciesMeta": {
"@types/react-native": {
"optional": true
}
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/eslint-parser": "^7.16.3",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.16.0",
"@types/jest": "^29.2.5",
"@types/react-native": "^0.70.8",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.48.0",
"codecov": "^3.1.0",
"eslint": "^8.2.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jest": "^27.0.1",
"eslint-plugin-react": "^7.27.0",
"eslint-plugin-react-native": "^4.0.0",
"flow-bin": "^0.196.0",
"jest": "^29.0.1",
"metro-babel-register": "^0.73.1",
"react": "^18.0.0",
"react-native": "^0.70.0",
"react-test-renderer": "^18.0.0"
"react-test-renderer": "^18.0.0",
"typescript": "^4.9.4"
},
"dependencies": {},
"eslintConfig": {
"parser": "@babel/eslint-parser",
"parser": "@typescript-eslint/parser",
"env": {
"jest": true,
"react-native/react-native": true
},
"plugins": [
"react",
"react-native",
"flowtype"
"import",
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:flowtype/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:import/errors",
"plugin:import/react-native"
"plugin:import/react-native",
"plugin:import/typescript"
],
"parserOptions": {
"ecmaFeatures": {
Expand All @@ -93,8 +111,7 @@
},
"settings": {
"react": {
"version": "detect",
"flowVersion": "detect"
"version": "detect"
},
"import/ignore": [
"react-native"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// @flow
import React, { type Node } from 'react'
import React from 'react'
import {
SafeAreaView,
Text,
Expand All @@ -9,9 +8,9 @@ import {

import styles from './styles'

export type Props = { error: Error, resetError: Function }
export type Props = { error: Error, resetError: () => void }

const FallbackComponent = (props: Props): Node => (
const FallbackComponent = (props: Props) => (
<SafeAreaView style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Oops!</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @flow
import { StyleSheet } from 'react-native'

const styles: any = StyleSheet.create({
const styles = StyleSheet.create({
container: {
backgroundColor: '#fafafa',
flex: 1,
Expand Down
15 changes: 7 additions & 8 deletions src/ErrorBoundary/index.js → src/ErrorBoundary/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// @flow
import React, { type Node, type ComponentType } from 'react'
import React, { type ComponentType } from 'react'

import FallbackComponent, {
type Props as FallbackComponentProps
} from './FallbackComponent'

type Props = {
children: Node,
export type Props = {
children: JSX.Element,
FallbackComponent: ComponentType<FallbackComponentProps>,
onError?: Function
onError?: (error: Error, stackTrace: string) => void
}

type State = { error: Error | null }
Expand All @@ -26,15 +25,15 @@ class ErrorBoundary extends React.Component<Props, State> {

componentDidCatch (error: Error, info: { componentStack: string }) {
if (typeof this.props.onError === 'function') {
this.props.onError.call(this, error, info.componentStack)
this.props.onError(error, info.componentStack)
}
}

resetError: Function = () => {
resetError: () => void = () => {
this.setState({ error: null })
}

render (): Node {
render () {
const { FallbackComponent } = this.props

return this.state.error
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import React from 'react'
import { Text, View } from 'react-native'
import renderer from 'react-test-renderer'
import renderer, { ReactTestInstance } from 'react-test-renderer'

import moduleIndex from '../index'
import ErrorBoundary from '../ErrorBoundary'

describe('ErrorBoundary', () => {
let consoleErrorSpy
let errorMock
let consoleErrorSpy: jest.SpyInstance
let errorMock: Error

const ComponentWithError = (props: { error: Error }) => (
<View>
{/* @ts-expect-error - We are rendering an error to trigger the ErrorBoundary component, even though it's not a valid React children. */}
{props.error}
</View>
)

beforeAll(() => {
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn())
errorMock = new Error('This is a test error!')
})

Expand Down Expand Up @@ -41,7 +48,7 @@ describe('ErrorBoundary', () => {
it('should catch the error and render the default FallbackComponent', () => {
const wrapper = renderer.create(
<ErrorBoundary>
<View>{errorMock}</View>
<ComponentWithError error={errorMock} />
</ErrorBoundary>
)

Expand All @@ -54,7 +61,7 @@ describe('ErrorBoundary', () => {
const FallbackComponent = () => <Text>Error!</Text>
const wrapper = renderer.create(
<ErrorBoundary FallbackComponent={FallbackComponent}>
<View>{errorMock}</View>
<ComponentWithError error={errorMock} />
</ErrorBoundary>
)

Expand All @@ -68,7 +75,7 @@ describe('ErrorBoundary', () => {

renderer.create(
<ErrorBoundary onError={onError}>
<View>{errorMock}</View>
<ComponentWithError error={errorMock} />
</ErrorBoundary>
)

Expand All @@ -84,10 +91,12 @@ describe('ErrorBoundary', () => {
</ErrorBoundary>
)

wrapper.getInstance().setState({ error: errorMock })
wrapper.getInstance().resetError()
const instance = wrapper.getInstance() as ReactTestInstance['instance']

instance.setState({ error: errorMock })
instance.resetError()

expect(wrapper.getInstance().state.error).toBeNull()
expect(instance.state.error).toBeNull()
})
})
})
Expand Down
11 changes: 0 additions & 11 deletions src/index.d.ts

This file was deleted.

4 changes: 0 additions & 4 deletions src/index.js

This file was deleted.

6 changes: 6 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import ErrorBoundary from './ErrorBoundary'

export type { Props as ErrorBoundaryProps } from './ErrorBoundary'
export type { Props as FallbackComponentProps } from './ErrorBoundary/FallbackComponent'

export default ErrorBoundary
38 changes: 38 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"baseUrl": "./",
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"importsNotUsedAsValues": "error",
"jsx": "react-native",
"lib": ["esnext"],
"module": "esnext",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitUseStrict": false,
"noStrictGenericChecks": false,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "lib",
"paths": {
"react-native-error-boundary": ["./src/index"]
},
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"target": "esnext"
},
"include": [
"src/**/*"
],
"exclude": [
"src/__tests__"
]
}
Loading

1 comment on commit c88b6c7

@vercel
Copy link

@vercel vercel bot commented on c88b6c7 Jan 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.