How to add sourcemap in React Native for Production? - reactjs

I received error log like the following while the app crashed:
Fatal Exception: com.facebook.react.modules.core.JavascriptException:
onSelect index.android.bundle:20:7148 onPress
index.android.bundle:20:2435
But it's not really helpful for me to trouble shoot. How could I enable source map so that I could track down where the issue is ?
UPDATE 2018
https://docs.expo.io/versions/latest/guides/using-sentry.html Looks promising !

For source mapping here is the way I go about it:
In my bundle command for my production build I tell it to generate a source map:
iOS:
react-native bundle --platform ios --entry-file index.ios.js --dev false --bundle-output ./ios/main.jsbundle --assets-dest ./ios --sourcemap-output ./sourcemap.js
Android - I had to actually modify the android/app/react.gradle file to get source maps generating on release compile. There might be an easier way but basically you find where it builds up the bundle command in the bundleReleaseJsAndAssets method and add the source map bit to it:
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "cmd","/c", "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file",
entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease, "--sourcemap-output", file("$buildDir/../../../sourcemap.js")
} else {
commandLine "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file",
entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease, "--sourcemap-output", file("$buildDir/../../../sourcemap.js")
}
The output path looks a bit odd but that puts it at your root level (same spot as iOS. I wanted it that way. You can obviously put it anywhere).
Then once you have an error with the line number that means nothing you run it through the "source-map" NPM package. You could probably get very elaborate with your approach but I simply went with:
var sourceMap = require('source-map');
var fs = require('fs');
fs.readFile('./sourcemap.js', 'utf8', function (err, data) {
var smc = new sourceMap.SourceMapConsumer(data);
console.log(smc.originalPositionFor({
line: 16,
column: 29356
}));
});
Where line and column should be replaced withe line and column number from your example output above.
This obviously works best if you have the source maps stored somewhere as the line and column numbers change from build to build as your code changes. It should get pretty close though if you can use you source control setup of choice to go back to the commit that was used to build the app in question and re-generate the bundle with the additional bits to the command to generate the source map.

Android inspired by #chetstone's answer
Starting on v0.32 for android, you can modify your android/app/build.gradle to accomplish this.
Look for the line
apply from: "../../node_modules/react-native/react.gradle"
Just above this, you will see something like:
project.ext.react = [
entryFile: "index.js",
]
Modify it to match the following
project.ext.react = [
entryFile: "index.js",
extraPackagerArgs: ["--sourcemap-output", file("$buildDir/../../../sourcemap.android.js")]
]
On iOS
Go to your build phases in Xcode for the "Bundle React Native code and images" phase and add:
export EXTRA_PACKAGER_ARGS="--sourcemap-output sourcemap.ios.js"

As noted, there's no obvious way to generate the sourcemap file for React Native on iOS. The bundle command is called from react-native-xcode.sh, and there's no provision to add parameters to the bundle command line. But I found a clean way to do it.
react-native-xcode.sh uses the environment variable BUNDLE_CONFIG to specify a config file. If you create an empty config file it has no effect, and then you can add additional CLI parameters.
Create an empty config file.
touch null_config
Set BUNDLE_CONFIG with your config file, and piggyback the --sourcemap-output parameter.
export BUNDLE_CONFIG="null_config --sourcemap-output ./sourcemap.js.map"
When you build, the file sourcemap.js.map will be created.

This is only for iOS.
step 1: Generate sourcemap.js file by using following command.
add this line in package.json file
"bundle:ios": "mkdir -p ios/{Bundle,source-map}; react-native bundle --platform ios --entry-file index.js --dev false --bundle-output ios/Bundle/main.jsbundle --assets-dest ios/Bundle --sourcemap-output ios/source-map/sourcemap.js"
Run this command, it will create sourcemap.js file under $PROJECT_DIR/ios/source-map/ folder
$ yarn bundle:ios
Step 2: Create a file sourcemap-decoder.js under $PROJECT_DIR/ios/source-map/
$ cd ios/source-map/
$ touch sourcemap-decoder.js
Content of sourcemap-decoder.js is
const sourceMap = require('source-map'); //(install- npm i source-map)
const fs = require('fs');
const path = require('path');
fs.readFile(path.resolve(__dirname, 'sourcemap.js'), 'utf8', async (err, data) => {
if (err) {
console.log('err', err,);
}
const consumer = await new sourceMap.SourceMapConsumer(JSON.parse(data));
console.log(consumer.originalPositionFor({
line: 1408,
column: 7762
}));
});
Step 3: execute the script for decoding
$ node ios/source-map/sourcemap-decoder.js

Related

React - synchronous txt file to string?

I'm absolutely baffled by this one...
In my React project with create-react-app, I have a standalone js file in which I'd like to read a string from a txt file. The txt file is part of a project and not on a server.
I can't seem to find any answers of how to complete this seemingly trivial task in a synchronous manner. These would seem like obvious options:
import text from './data/text.txt';
const text = require('./data/text.txt');
Both lines above return a new path that includes /static/media/, which I can access through localhost in the browser, but that doesn't help me.
I can use JSON but it's almost a matter of principle at this point. It just seems ridiculous that I can't read a simple txt file.
You can use webpack raw-loader to directly import raw files into your project.
Install:
$ npm install raw-loader --save-dev
Config:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.txt$/i,
use: 'raw-loader',
},
],
},
};
Usage:
import text from './data/text.txt';
console.log(text); // This line will print out the content of the text file in the console

How can I disable chunk(code splitting) with webpack4?

I created react project with : yarn create react-app. I'm using webpack 4.29.6, react 16.8.6.
I want to disable code splitting because I want just one 'main.js' file in build/static/js folder. (I already removed hash from their names.)
I tried:
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
}),
and
splitChunks: {
chunks: 'all',
name: false,
cacheGroups: {
default:false
}
}
but these solutions give me these files:
build
/static
/js
-2.chunk.js
-2.chunk.js.map
-main.chunk.js
-main.chunk.js.map
-runtime~main.js
-runtime~main.js.map
I think runtime~main.js file and .map file are just okay, but I want the exact 'main.js' file without chunk, without 2.chunk.js. How can I disable default code splitting of webpack 4?
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
})
]
Try if this works. Install this manually LimitChunkCountPlugin
https://webpack.js.org/plugins/limit-chunk-count-plugin/
If you created your project with create-react-app and haven't yet ejected, it appears you have two options currently available:
Option 1 - Create a custom build script
First, install the package rewire as a dependency:
npm install rewire
Create a scripts folder, inside create a new file and give it a name e.g., build-non-split.js, then add:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
let config = defaults.__get__('config');
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
config.optimization.runtimeChunk = false;
Modify package.json, inside "scripts" and replace build with:
"build": "node ./scripts/build-non-split.js",
Finally, run npm run build or yarn build
Option 2 - Install the package react-app-rewire-disable-chunks
Note: If you go with this option, don't forget to replace the build script with react-app-rewired. Like this:
"build": "react-app-rewired build",
Source: Disabling code splitting in CRA2 #5306

How to configure react-script so that it doesn't override tsconfig.json on 'start'

I'm currently using create-react-app to bootstrap one of my projects. Basically, I'm trying to set up paths in tsconfig.json by adding these to the default tsconfig.json generated by create-react-app:
"baseUrl": "./src",
"paths": {
"interfaces/*": [
"common/interfaces/*",
],
"components/*": [
"common/components/*",
],
},
However, every time I run yarn start which basically runs react-scripts start, it deletes my changes and generates the default configurations again.
How can I tell create-react-app to use my custom configs?
I was able to do this by using advice from this issue.
Put the configuration options react scripts likes to remove in a separate file (e.g. paths.json) and reference it from tsconfig.json via the extends directive.
paths.json:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"interfaces/*": [ "common/interfaces/*"],
"components/*": [ "common/components/*"],
}
}
}
tsconfig.json
{
"extends": "./paths.json"
...rest of tsconfig.json
}
Create React App does not currently support baseUrl. However there is a workaround...to setup baseUrl for both webpack and the IDE you have to do the following:
Create a .env file with the following code:
NODE_PATH=./
Create a tsconfig.paths.json file with the following code inside:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"src/*": ["*"]
}
}
}
Add the following line to tsconfig.json
{
"extends": "./tsconfig.paths.json",
...
}
You can't and I am unsure when you will be able to. I have been trying to use baseUrl and paths so I can avoid relative imports but as you can see they are intentionally removing certain values. The "(yet)" is encouraging but (sigh) who knows when they will officially be supporting it. I recommend subscribing to this github issue to be alerted if/when this changes.
The following changes are being made to your tsconfig.json file:
- compilerOptions.baseUrl must not be set (absolute imports are not supported (yet))
- compilerOptions.paths must not be set (aliased imports are not supported)
If you are using react-scripts 4.0.0 like me then all you need to do is remove the line (around line 160 on my end):
paths: { value: undefined, reason: 'aliased imports are not supported' }
from the file node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
I was able to straight up add my baseUrl and paths config to my tsconfig.json file like so:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#domain/*": ["../src/domain/*"],
},
}
}
and finally compile and move on with my life.
Per usual, YMMV. Please test your stuff. This is obviously a hack but it worked for me so I'm posting here in case it helps someone.
Here's a patch if you feel like sharing with your team:
diff --git a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
index 00139ee..5ccf099 100644
--- a/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
+++ b/node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
## -156,7 +156,8 ## function verifyTypeScriptSetup() {
: 'react',
reason: 'to support the new JSX transform in React 17',
},
- paths: { value: undefined, reason: 'aliased imports are not supported' },
+ // Removed this line so I can add paths to my tsconfig file
+ // paths: { value: undefined, reason: 'aliased imports are not supported' },
};
Edit
Per #Bartekus thoughtful suggestion in the comments thread I'm adding information on the package I use when I need to add (possibly) temporary changes like these to an npm package: patch-package
The package essentially provides a way to make changes to a package in a cleaner way. Especially when you consider collaboration it becomes very cumbersome to directly change an npm file and move on. The next time you update that package or even when you start developing in a new machine and run npm install your changes will be lost. Also, if you have teammates working on the same project they would never inherit the changes.
In essence you go through the following steps to patch a package:
# fix a bug in one of your dependencies
vim node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js
# run patch-package to create a .patch file
npx patch-package react-scripts
# commit the patch file to share the fix with your team
git add patches/react-scripts+4.0.0.patch
git commit -m "Enable aliased imports in react-scripts"
Next time someone checks out the project and installs it, the patch will be applied automatically due to a post-install script you add during set up:
"scripts": {
+ "postinstall": "patch-package"
}
See up to date instructions in the package's documentation
I had a similar issue to this general problem (CRA overwrites "noEmit": false in my tsconfig.json of a React library I'm working on where I have two separate builds, one for local development, and another to build the production library with typings). Simple solution: use sed in a postbuild script in the package.json. For example: In-place edits with sed on OS X .
{
...
"scripts": {
...
"postbuild": "sed -i '' 's/{THING CRA IS REPLACING}/{WHAT YOU ACTUALLY WANT}/g' tsconfig.json # CRA is too opinionated on this one.",
...
}
...
}
This approach, however, is not cross-platform (unlike how rimraf is the cross-platform alternative to rm -rf).
For me, the problem was with VSCode using an older version of typescript (4.0.3), while the typescript version shipped with the project is (4.1.2).
The following did the trick for me:
Go to the command palette CTRL+Shift+P.
Choose "TypeScript: Select a TypeScript Version...".
Choose "Use workspace Version".
On Botpress (with react-scripts 4.0.3), we use a combination of 2 tricks to use paths without ejecting or patching the code. As Glenn and Microcipcip said, the first step is to extend the tsconfig.json file
tsconfig.path.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"~/*": ["src/*"],
"common/*": ["../bp/src/common/*"]
}
}
}
tsconfig.json
{
...
"extends": "./tsconfig.paths.json"
}
Then to make it work in the background, use the package react-app-rewired. It allows to make slight adjustments to the webpack configuration without actually ejecting CRA.
config-overrides.js
module.exports = {
webpack: (config, env) => {
config.resolve.alias['common'] = path.join(__dirname, '../bp/dist/common')
config.resolve.alias['~'] = path.join(__dirname, './src')
}
}
To see the full code, you can check the github repository https://github.com/botpress/botpress/tree/master/packages/ui-admin
For macOS this workaround should work.
package.json
"scripts": {
"start": "osascript -e 'tell app \"Terminal\" to do script \"cd $PATH_TO_REACT_APP && node ./setNoEmitFalse\"' && react-scripts start",
...
},
...
setNoEmitFalse.js
const fs = require('fs');
const { sleep } = require('sleep')
const path = './tsconfig.json'
const run = async () => {
sleep(2)
const tsconfig = fs.readFileSync(path, 'utf-8');
const fixed = tsconfig.replace('"noEmit": true', '"noEmit": false');
fs.writeFileSync(path, fixed)
}
run()
The execution of the javascript file in a separate terminal (osascript) provides the normal output for react-scripts in the original terminal.
Go to node_modules/react-scripts/scripts/utils/verifyTypeScriptSetup.js and replace
const compilerOptions = {
...
};
by
const compilerOptions = { };

Is it good to use package.json file in react component file?

I have to use version mentioned in package.json file in front-end(react js) file.
{
"name": "asdfg",
"version": "3.5.2", // want to use this
"description": "description",
"scripts": {}
//etc etc etc
......
}
Send package.json [version] to Angularjs Front end for display purposes
I'd gone through above post and found two ways for the same. but none of them I was asked to implement.
#1. During build process
#2. By creating endpoint
So I want to know the approach below is valid/good or not ?
react-front-end-file.js
import packageJson from '../package.json'; // imported
...
...
// Usage which gives me version - 3.5.2
<div className='app-version'>{packageJson.version}</div>
Let me know if this approach is fine.
The below 2 approaches seems to have either dependency or add an extra implentation which might not be needed
During build process - ( has dependency on module bundler like webpack etc.)
By creating endpoint - ( needs an extra code at server just to get version )
Instead, As package.json is a file which takes json object in it so you can use it to import json and use its any keys mentioned in that file ( version in your case but only constraint here is, you should have access to package.json file after application deployment, so dont forget to move file in deployment environment )
So your approach seems to be fine.
I would do something like this:
In your module bundler, require your package json file and define a global variable and use it wherever you want
e.g. I do something like this in webpack:
const packageJson = require('./package.json')
const plugins = [
new webpack.DefinePlugin(
{
'__APPVERSION__': JSON.stringify(packageJson.version)
}
)
]
React Component:
<div className='app-version'>{__APPVERSION__}</div>

`_Symbol.'for'`: Is that actually valid ES6? Webpack built it from React source

I'm trying to take React 0.14 for a spin before I upgrade it in my project. However, with a simple "hello world" prototype, Webpack is throwing an error:
ERROR in ./~/react/lib/ReactElement.js
Module parse failed: /home/dan/Demos/reactiflux/node_modules/babel-loader/index.js!/home/dan/Demos/reactiflux/node_modules/react/lib/ReactElement.js Line 25: Unexpected string
You may need an appropriate loader to handle this file type.
| // The Symbol used to tag the ReactElement type. If there is no native Symbol
| // nor polyfill, then a plain number is used for performance.
| var REACT_ELEMENT_TYPE = typeof _Symbol === 'function' && _Symbol.'for' && _Symbol.'for'('react.element') || 0xeac7;
|
| var RESERVED_PROPS = {
# ./~/react/lib/ReactMount.js 18:19-44
I do have babel-loader configured, and when I downgrade to React 0.13, everything works. What really stands out to me, is _Symbol.'for', in the middle of the error message.
In react/lib/ReactElement.js on line 21 (not 25), that line looks much more correct, with square brackets around the 'for' key:
var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol['for'] && Symbol['for']('react.element') || 0xeac7;
I assume that the code shown in the error message is either in an intermediate state during compilation, or is the final compiled output. Does anyone know what could cause Webpack to produce something that looks so wrong? Has anyone successfully used Webpack, Babel and React ~0.14.1 together yet?
update
There is an issue for this: https://github.com/babel/babel/issues/2377
It's closed, but it looks like it came back for me. This was fixed in 5.8.25, but I have 5.8.29 and I still hit the bug.
It appears that the problem has something to do with me including babel runtime. My .babelrc was copied from an older project:
{
"optional": "runtime",
"stage": 0
}
In this little hello-world demo, there is nothing that requires bundling the runtime, so I just removed it, after noticing that https://github.com/DominicTobias/universal-react/, which also uses the same build tools, does not need it. That was the only change I needed to make to get this to build.
My webpack config is super simple:
var path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.join(__dirname, "/dist"),
filename: "index.min.js"
},
module: {
loaders: [{
test: /\.js$/,
loader: "babel"
}]
}
};
I guess that's what I get for copying a config file from a more complex project into what was supposed to be a simplest possible demo.
I see that there is a babel-plugin-runtime as well as a babel-runtime on NPM, but when I tried out BPR for the sake of completeness, Babel complains: Module build failed: ReferenceError: The plugin "runtime" collides with another of the same name. Since I don't actually need the runtime, the linked GH repo is a 404, and since this really belongs in the issue trackers after all, this is as far as I am going to take this for now.
No, that is not valid code. That was an issue in Babel project, but it has been fixed in the 6.0 version which was released recently.
I was run into this issue too, and now I have checked this with latest version, and it is works fine. Here is my test steps:
# install Babel and plugins
npm install babel-cli babel-preset-es2015 babel-plugin-transform-runtime
# install React
npm install react
# run babel against problem react file
./node_modules/.bin/babel node_modules/react/lib/ReactElement.js --plugins transform-runtime --presets es2015
It is provides valid output, so the issue seems to be resolved.
And there is good news for you, babel-loader for webpack already supports 6 version of Babel. Check out its docs for details

Resources