I am trying to run Storybook, with Typescript, Expo and react-native-web. Everything is fine until I use the expo-linear-gradient package. When i launch Storybook I get the following error
ERROR in ./node_modules/expo-linear-gradient/build/LinearGradient.js
Module not found: Error: Can't resolve './NativeLinearGradient' in '.../node_modules/expo-linear-gradient/build'
I’m assuming this is something to do with that the file extensions for the NativeLinearGradient are either .web.ts, .ios.ts or android.ts, and being new to development I’m a little lost as to how to resolve this, so was hoping someone might be able to help or provide a point of reference for me to learn. This project is being built with a boilerplate, but that is only because I could not configure react-native-web and typescript properly to convert a flow project.
The boiler plate can be found here - https://github.com/Naturalclar/expo-typescript-starter
I have not modified the webpack.config.js or tsconfig.json, these are below. I have tried using the suggested configurations from Storybook, with awesome-typescript-loader, but they through even more errors with expo modules, such as ‘module not parsed’ and ‘module not found’, so this seems to be the less troublesome approach for me, whilst I learn.
If anyone has any experience with storybook, typescript and react-native-web being used with Expo and can help I’d greatly appreciate any thoughts.
If you need any more information please let me know what to provide.
webpack.config.js
const path = require('path');
const threadLoader = require('thread-loader');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const jsWorkerCommonOptions = {
workers: 2,
workerParallelJobs: 50,
poolParallelJobs: 50
};
const babelWorkerOptions = {
...jsWorkerCommonOptions,
name: 'babel-pool'
};
const tsWorkerOptions = {
...jsWorkerCommonOptions,
name: 'ts-pool'
};
module.exports = ({ config, mode }) => {
if (mode !== 'PRODUCTION') {
threadLoader.warmup(babelWorkerOptions, ['babel-loader']);
threadLoader.warmup(tsWorkerOptions, ['ts-loader']);
}
config.module.rules.push({
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{ loader: 'cache-loader' },
{ loader: 'thread-loader', options: tsWorkerOptions },
{
loader: 'ts-loader',
options: {
experimentalWatchApi: true,
transpileOnly: true,
happyPackMode: true
}
},
{ loader: 'react-docgen-typescript-loader' }
]
});
// type-checking
config.plugins.push(
new ForkTsCheckerWebpackPlugin({ checkSyntacticErrors: true })
);
config.module.rules.push({
test: /\.jsx?$/,
include: [
path.resolve(__dirname, '../node_modules/react-native-paper'),
path.resolve(__dirname, '../node_modules/react-native-elements'),
path.resolve(__dirname, '../node_modules/react-native-safe-area-view'),
path.resolve(__dirname, '../node_modules/react-native-vector-icons'),
path.resolve(__dirname, '../node_modules/@expo/vector-icons'),
path.resolve(__dirname, '../node_modules/expo-linear-gradient'),
path.resolve(__dirname, '../node_modules/react-native-ratings'),
path.resolve(__dirname, '../node_modules/react-native-status-bar-height')
],
use: [
{ loader: 'cache-loader' },
{ loader: 'thread-loader', options: babelWorkerOptions },
{
loader: 'babel-loader?cacheDirectory?true',
options: {
presets: [
'module:metro-react-native-babel-preset',
'@babel/preset-flow'
]
}
}
]
});
// react-native を import している箇所を react-native-web に変換
config.resolve.alias['react-native$'] = require.resolve('react-native-web');
config.resolve.alias['@expo/vector-icons'] = path.resolve(
__dirname,
'../node_modules/react-native-vector-icons'
);
// .ts, .tsx を含めるように追加
config.resolve.extensions.push('.ts', '.tsx');
return config;
};
tsconfig.json
{
"compilerOptions": {
"outDir": "build/lib",
"module": "commonjs",
"target": "es5",
"lib": ["es5", "es6", "es7", "es2017", "dom"],
"sourceMap": true,
"allowJs": false,
"jsx": "preserve",
"moduleResolution": "node",
"rootDirs": ["src", "stories"],
"baseUrl": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"declaration": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build", "scripts"]
}
package.json
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject",
"test": "node ./node_modules/jest/bin/jest.js --watchAll",
"lint": "eslint . --ext '.ts, .tsx'",
"type-check": "tsc --noEmit",
"new": "scaffdog generate",
"storybook": "start-storybook -h 192.168.1.64 -p 9001 -c .storybook -s .storybook/assets",
"storybook:build": "build-storybook -c .storybook -s .storybook/assets",
"klank": "klank storybook-static/index.html"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/react-native-action-sheet": "^3.0.3",
"@expo/samples": "3.0.3",
"@storybook/addon-storyshots": "^5.1.10",
"expo": "^34.0.4",
"expo-asset": "^6.0.0",
"expo-camera": "~6.0.0",
"expo-font": "~6.0.1",
"expo-linear-gradient": "~6.0.0",
"klank": "^0.0.5",
"react": "16.8.6",
"react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
"react-native-extended-stylesheet": "^0.12.0",
"react-native-linear-gradient": "^2.5.6",
"react-native-paper": "^2.16.0",
"react-native-vector-icons": "^6.6.0",
"react-navigation": "^3.11.1"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@emotion/core": "^10.0.15",
"@storybook/addon-actions": "^5.1.10",
"@storybook/addon-info": "^5.1.10",
"@storybook/addon-knobs": "^5.1.10",
"@storybook/addon-options": "^5.1.10",
"@storybook/addon-viewport": "^5.1.10",
"@storybook/react": "^5.1.10",
"@storybook/theming": "^5.1.10",
"@types/expo": "^32.0.13",
"@types/expo__vector-icons": "^9.0.1",
"@types/jest": "^24.0.17",
"@types/react": "^16.8.25",
"@types/react-native": "^0.60.3",
"@types/react-navigation": "^3.0.8",
"@types/react-test-renderer": "^16.8.3",
"@types/storybook__addon-actions": "^3.4.3",
"@types/storybook__addon-knobs": "^5.0.3",
"@types/storybook__react": "^4.0.2",
"@typescript-eslint/eslint-plugin": "^1.13.0",
"@typescript-eslint/parser": "^1.13.0",
"awesome-typescript-loader": "^5.2.1",
"babel-loader": "^8.0.6",
"babel-preset-expo": "^5.2.0",
"cache-loader": "^4.1.0",
"eslint": "^6.1.0",
"eslint-config-airbnb": "^17.1.1",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-react": "^7.14.3",
"eslint-plugin-typescript": "^0.14.0",
"fork-ts-checker-webpack-plugin": "^1.5.0",
"jest-expo": "^34.0.0",
"metro-react-native-babel-preset": "^0.55.0",
"node-fetch": "^2.6.0",
"prettier": "^1.18.2",
"react-art": "16.8.6",
"react-docgen-typescript-loader": "^3.1.0",
"react-dom": "16.8.6",
"react-native-elements": "^1.1.0",
"react-native-web": "^0.11.5",
"react-test-renderer": "^16.8.6",
"redux-logger": "^3.0.6",
"scaffdog": "^0.1.2",
"thread-loader": "^2.1.2",
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"typescript": "^3.5.3"
},
"private": true
}