I started a new React Native project (v61.5) with the TypeScript template (npx react-native init MyTSProject --template react-native-template-typescript
). It compiles fine with React Native but when I try to add webpack (for React Native Web) I get JSX/TSX errors (I think). Am I doing something wrong with webpack here?
tsconfig.json
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": [ "es5", "es6", "dom" ], /* Specify library files to be included in the compilation. */
"allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react-native", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
"incremental": true, /* Enable incremental compilation */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
"resolveJsonModule": true,
},
"exclude": [
"node_modules", "babel.config.js", "metro.config.js", "jest.config.js"
]
}
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const rootDir = path.join(__dirname, '..');
const webpackEnv = process.env.NODE_ENV || 'development';
module.exports = {
mode: webpackEnv,
entry: {
app: path.join(rootDir, './index.web.ts'),
},
output: {
path: path.resolve(rootDir, 'dist'),
filename: 'app-[hash].bundle.js',
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(tsx|ts|jsx|js|mjs)$/,
exclude: /node_modules/,
use: [ 'ts-loader' ],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, './index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
],
resolve: {
extensions: [
'.web.tsx',
'.web.ts',
'.tsx',
'.ts',
'.web.jsx',
'.web.js',
'.jsx',
'.js',
], // read files in following order
alias: Object.assign({
'react-native$': 'react-native-web',
}),
},
};
TypeScript (As from the React Native demo app)
import React from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
} from 'react-native';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
declare var global: {HermesInternal: null | {}};
const App = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.scrollView}>
<Header />
{global.HermesInternal == null ? null : (
<View style={styles.engine}>
<Text style={styles.footer}>Engine: Hermes</Text>
</View>
)}
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Step One</Text>
<Text style={styles.sectionDescription}>
Edit <Text style={styles.highlight}>App.tsx</Text> to change this
screen and then come back to see your edits.
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>See Your Changes</Text>
<Text style={styles.sectionDescription}>
<ReloadInstructions />
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Debug</Text>
<Text style={styles.sectionDescription}>
<DebugInstructions />
</Text>
</View>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>Learn More</Text>
<Text style={styles.sectionDescription}>
Read the docs to discover what to do next:
</Text>
</View>
<LearnMoreLinks />
</View>
</ScrollView>
</SafeAreaView>
</>
);
};
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
engine: {
position: 'absolute',
right: 0,
},
body: {
backgroundColor: Colors.white,
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: Colors.black,
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
color: Colors.dark,
},
highlight: {
fontWeight: '700',
},
footer: {
color: Colors.dark,
fontSize: 12,
fontWeight: '600',
padding: 4,
paddingRight: 12,
textAlign: 'right',
},
});
export default App;
Output/Error
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /home/nick/MyTSProject/web
✖ 「wdm」: Hash: e739fe310ea105fb05fe
Version: webpack 4.41.5
Time: 4672ms
Built at: 12/30/2019 8:13:55 PM
Asset Size Chunks Chunk Names
app-e739fe310ea105fb05fe.bundle.js 2.48 MiB app [emitted] [immutable] app
app-e739fe310ea105fb05fe.bundle.js.map 2.63 MiB app [emitted] [dev] app
index.html 438 bytes [emitted]
Entrypoint app = app-e739fe310ea105fb05fe.bundle.js app-e739fe310ea105fb05fe.bundle.js.map
[0] multi ../node_modules/webpack-dev-server/client?http://localhost:8080 ../index.web.ts 40 bytes {app} [built]
[../app.json] 60 bytes {app} [built]
[../index.web.ts] 496 bytes {app} [built]
[../node_modules/react-native-web/dist/index.js] 4.3 KiB {app} [built]
[../node_modules/tslib/tslib.es6.js] 9.21 KiB {app} [built]
[../node_modules/webpack-dev-server/client/index.js?http://localhost:8080] ../node_modules/webpack-dev-server/client?http://localhost:8080 4.29 KiB {app} [built]
[../node_modules/webpack-dev-server/client/overlay.js] 3.51 KiB {app} [built]
[../node_modules/webpack-dev-server/client/socket.js] 1.53 KiB {app} [built]
[../node_modules/webpack-dev-server/client/utils/createSocketUrl.js] 2.91 KiB {app} [built]
[../node_modules/webpack-dev-server/client/utils/log.js] 964 bytes {app} [built]
[../node_modules/webpack-dev-server/client/utils/reloadApp.js] 1.59 KiB {app} [built]
[../node_modules/webpack-dev-server/client/utils/sendMessage.js] 402 bytes {app} [built]
[../node_modules/webpack-dev-server/node_modules/strip-ansi/index.js] 161 bytes {app} [built]
[../node_modules/webpack/hot sync ^\.\/log$] ../node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {app} [built]
[../source/App.tsx] 438 bytes {app} [built] [failed] [1 error]
+ 255 hidden modules
ERROR in ../source/App.tsx 8:12
Module parse failed: Unexpected token (8:12)
File was processed with these loaders:
* ../node_modules/ts-loader/index.js
You may need an additional loader to handle the result of these loaders.
| var NewAppScreen_1 = require("react-native/Libraries/NewAppScreen");
| var App = function () {
> return (<>
| <react_native_1.StatusBar barStyle="dark-content"/>
| <react_native_1.SafeAreaView>
@ ../index.web.ts 6:36-59
Child html-webpack-plugin for "index.html":
1 asset
Entrypoint undefined = index.html
[../node_modules/html-webpack-plugin/lib/loader.js!./index.html] 566 bytes {0} [built]
[../node_modules/lodash/lodash.js] 528 KiB {0} [built]
[../node_modules/webpack/buildin/global.js] 472 bytes {0} [built]
[../node_modules/webpack/buildin/module.js] 497 bytes {0} [built]
ℹ 「wdm」: Failed to compile.
I have tried alternative loaders (babel-loader, awesome-typescript-loader). I have tried various chat rooms. I have read many, many blog posts (although they almost all seem to be from years ago and no longer apply).
No idea where to go from here.