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

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>

Related

react-i18next with multiple translation-files

I have a little problem in my application.
I use i18next and react-i18next for the translation and have already included it.
The whole translation comes from 1 file for each language and that is a mess with over 4000 rows :(
Now I want update this so that i18next would take the translation files placed in the different component-folders and their children-folders.
The folder-structure should look like this after the update:
scr
- components
-- Header
---translations (en/translation.json, de/translation.json)
-- Dashboard
--- translations (en/translation.json, de/translation.json)
--- Menu
---- translations (en/translation.json, de/translation.json)
---- ExampleComponent.tsx
---- ...
--- Cards
---- translations (en/translation.json, de/translation.json)
...
I already figured out how I can handle the automatic export via babel and babel-i18next-plugin with the "namespace"
So, my code (example Menu) would be written like this:
const { t } = useTranslation("Dashboard/Menu")
const explString = t("ExampleComponent.ExampleString","This is an example")
In babel I placed the plugin like this:
[i18next-plugin, {"outputPath": "src/components/{{ns}}/translations/{{locale}}/translation.json"}]
This runs without problems. It takes the namespace as a folder-structure and places the translation-files into the translation-folder including the correct keys.
Now, how I can tell i18next, where to find the translation-files?
I could only figure out that I can import the files (file-by-file) inside a resource.
I tried backend plugins (html-backend, async-storage-backend, local-storage-backend and filesystem) with
backend: { loadPath: "components/{{ns}}/translations/{{lng}}/translation.json" }
(The i18next.ts is placed inside src/)
and I get the warnings that the keys aren't found.
Also, you can see that I use TypeScript.
In my webpack I tried it with the ts-i18next-loader with this inside the webpack configuration file:
{
test: /\translation.json$/,
exclude: /node_modules/,
loader: 'i18next-ts-loader',
options: {
localeFilesPattern: 'src/components/{{ns}}/translations/{{lng}}/translation.json',
},
},
If I only had 5-6 translation-files for each language / namespace it would not be a problem to put it inside the resource but at the end I have more than 100 files for each language.
Would be nice if anyone had a solution to my problem. If you need any further information I can update the post.
Cheers
There's an alternative plugin to be used, suggested in the official documentation: https://www.i18next.com/how-to/add-or-load-translations#lazy-load-in-memory-translations
i18next-resources-to-backend helps to transform resources to an i18next backend. This means, you can also lazy load translations, for example when using webpack:
import i18next from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
i18next
.use(resourcesToBackend((language, namespace, callback) => {
import(`./locales/${language}/${namespace}.json`)
.then((resources) => {
callback(null, resources)
})
.catch((error) => {
callback(error, null)
})
}))
.init({ /* other options */ })
Found the solution.
After i included the "webpack backend for i18next" it solved the problem and the translation gets the correct file.
i18next webpack backend by SimeonC

Export module via Webpack

I am working on a React app. In index.js am exporting some variables, e.g.
export const a="Hello";
export const b=[1,2,3];
In webpack.config.js:
...
output: {
...
library: "myApp",
libraryTarget: "window"
},
...
I know window.myApp is now a module which includes all the exported variables from index.js. What I do not know is how the above works. How this module is being created and why it includes only the exports from index.js and not other files as well? How, may I include exports from another specific file?
Your module is being created based on the entry configuration and the other modules and plugins that you configure and is converted to a build file based on the configuration provided in the output config of webpack.
library setup is tied to the entry configuration so if you specify the entry to be index.js, your exports from within the index.js are available within the build
In order to also expose exports from other files, you can import and export them from the index file like
export { default as SomeFunction} from 'some-function.js';
According to the webpack docs:
For
most libraries, specifying a single entry point is sufficient. While
multi-part libraries are possible, it is simpler to expose partial
exports through an index script that serves as a single entry point.
Using an array as an entry point for a library is not recommended.
libraryTarget specifies how the module is exposed. For instance in your case your module is exposed on the window object.
You can expose the library in the following ways:
Variable: as a global variable made available by a script tag (libraryTarget:'var').
This: available through the this object (libraryTarget:'this').
Window: available through the window object, in the browser (libraryTarget:'window').
UMD: available after AMD or CommonJS require (libraryTarget:'umd').
If library is set and libraryTarget is not, libraryTarget defaults to
var as specified in the output configuration documentation. See
output.libraryTarget there for a detailed list of all available
options.
You have two solution to include other files
First is to use multiple entries in your webpack module
module.exports = {
// mode: "development || "production",
entry: {
A: "./CompA",
B: "./CompB"
},
Second approach is use one entry like your solution, but need to add wrappere.js
wrapper.js
import * as a from './CompA.js'
import * as b from './CompB.js'
export a,b
And your wrapper become the single entry, where you can access later wrapper.a,wrapper.b
module.exports = {
// mode: "development || "production",
entry: {
app: "./wrapper.js",
},

TypeScript with Relay: Can't resolve generated module

In my MessageItem.tsx component I have the following code:
const data = useFragment(
graphql`
fragment MessageItem_message on Message {
date
body
}
`,
message as any
);
After running relay-compiler --src ./src --schema ../../schema.graphql --language typescript --artifactDirectory ./src/__generated__, a module named MessageItem_message.graphql.ts gets generated.
But when I run the app it gives me an error:
Failed to compile.
./src/components/MessageItem.tsx
Module not found: Can't resolve
'./__generated__/MessageItem_message.graphql'
The reason is only components at the src root can refer to the right path (./__generated__), whereas components in a folder actually need to refer to the path (../__generated__) but it's not doing so.
How can I configure the path?
Edit .babelrc to point to the artifactDirectory
// .babelrc
{
"plugins": [
[
"relay",
{
"artifactDirectory": "./src/ui/graphql/types"
}
]
]
}
Remove "--artifactDirectory ./src/__generated__" from the relay-compiler options.
By default it seems the Relay compiler puts a "__generated__" directory in the directory with any source code containing GraphQL.
As a result any "./__generated__" references anywhere and at any level in the source code hierarchy now work as they should.
Thanks to #ThibaultBoursier for the pointer.
PS I wonder if the --artifcactDirectory option is just meant to be used to change the name of the artifact directory, rather than its location?
Just moments ago I ran into the same issue. The reason is that the relay-compiler is using the artifactDirectory setting to decide where to put the generated files, but the babel-plugin-relay exposing the graphql tag needs to get the very same argument otherwise it just attempts to include a colocated relative file.
I fixed it in my case by configuring the plugin with a babel-plugin-macros.config.js file as follows (where the artifactDirectory is the same as the one supplied to the relay-compiler):
module.exports = {
relay: {
artifactDirectory: "./src/ui/graphql/types",
},
};
This solution assumes you are using the macro via babel-plugin-macros, otherwise you might need to supply that argument via the .babelrc file but I have no experience with that unfortunately.

Shared component library best practices

I am creating a shareable React component library.
The library contains many components but the end user may only need to use a few of them.
When you bundle code with Webpack (or Parcel or Rollup) it creates one single file with all the code.
For performance reasons I do not want to all that code to be downloaded by the browser unless it is actually used.
Am I right in thinking that I should not bundle the components? Should the bundling be left to the consumer of the components?
Do I leave anything else to the consumer of the components? Do I just transpile the JSX and that's it?
If the same repo contains lots of different components, what should be in main.js?
This is an extremely long answer because this question deserves an extremely long and detailed answer as the "best practice" way is more complicated than just a few-line response.
I've maintained our in-house libraries for 3.5+ years in that time I've settled on two ways I think libraries should be bundled the trade-offs depend on how big your library is and personally we compile both ways to please both subsets of consumers.
Method 1: Create an index.ts file with everything you want to be exposed exported and target rollup at this file as its input. Bundle your entire library into a single index.js file and index.css file; With external dependencies inherited from the consumer project to avoid duplication of library code.
(gist included at bottom of example config)
Pros: Easy to consume as project consumers can import everything from the root relative library path import { Foo, Bar } from "library"
Cons: This will never be tree shakable, and before people say to do this with ESM and it will be tree shakeable. NextJS doesn't support ESM at this current stage and neither do a lot of project setups that's why it's still a good idea to compile this build to just CJS. If someone imports 1 of your components they will get all the CSS and all the javascript for all your components.
Method 2: This is for advanced users: Create a new file for every export and use rollup-plugin-multi-input with the option "preserveModules: true" depending on how what CSS system you're using your also need to make sure that your CSS is NOT merged into a single file but that each CSS file requires(".css") statement is left inside the output file after rollup and that CSS file exists.
Pros: When users import { Foo } from "library/dist/foo" they will
only get the code for Foo, and the CSS for Foo, and nothing more.
Cons: This setup involves the consumer having to handle node_modules
require(".css") statements in their build configuration with NextJS
this is done with next-transpile-modules npm package.
Caveat: We use our own babel plugin you can find it here: https://www.npmjs.com/package/babel-plugin-qubic to allow people to import { Foo, Bar } from "library" and then with babel transform it to...
import { Foo } from "library/dist/export/foo"
import { Bar } from "library/dist/export/bar"
We have multiple rollup configurations where we actually use both methods; so for library consumers who don't care for tree shaking can just do "Foo from "library" and import the single CSS file, and for library consumers who do care for tree shaking and only using critical CSS they can just turn on our babel plugin.
Rollup guide for best practice:
whether you are using typescript or not ALWAYS build with "rollup-plugin-babel": "5.0.0-alpha.1"
Make sure your .babelrc looks like this.
{
"presets": [
["#babel/preset-env", {
"targets": {"chrome": "58", "ie": "11"},
"useBuiltIns": false
}],
"#babel/preset-react",
"#babel/preset-typescript"
],
"plugins": [
["#babel/plugin-transform-runtime", {
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false,
"version": "^7.8.3"
}],
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-classes",
["#babel/plugin-proposal-optional-chaining", {
"loose": true
}]
]
}
And with the babel plugin in rollup looking like this...
babel({
babelHelpers: "runtime",
extensions,
include: ["src/**/*"],
exclude: "node_modules/**",
babelrc: true
}),
And your package.json looking ATLEAST like this:
"dependencies": {
"#babel/runtime": "^7.8.3",
"react": "^16.10.2",
"react-dom": "^16.10.2",
"regenerator-runtime": "^0.13.3"
},
"peerDependencies": {
"react": "^16.12.0",
"react-dom": "^16.12.0",
}
And finally your externals in rollup looking ATLEAST like this.
const makeExternalPredicate = externalArr => {
if (externalArr.length === 0) return () => false;
return id => new RegExp(`^(${externalArr.join('|')})($|/)`).test(id);
};
//... rest of rollup config above external.
external: makeExternalPredicate(Object.keys(pkg.peerDependencies || {}).concat(Object.keys(pkg.dependencies || {}))),
// rest of rollup config below external.
Why?
This will bundle your shit to automatically to inherit
react/react-dom and your other peer/external dependencies from the
consumer project meaning they won't be duplicated in your bundle.
This will bundle to ES5
This will automatically require("..") in all the babel helper functions for objectSpread, classes, etc FROM the consumer project which will wipe another 15-25KB from your bundle size and mean that the helper functions for objectSpread won't be duplicated in your library output + the consuming projects bundled output.
Async functions will still work
externals will match anything that starts with that peer-dependency suffix i.e babel-helpers will match external for babel-helpers/helpers/object-spread
Finally here is a gist for an example single index.js file output rollup config file.
https://gist.github.com/ShanonJackson/deb65ebf5b2094b3eac6141b9c25a0e3
Where the target src/export/index.ts looks like this...
export { Button } from "../components/Button/Button";
export * from "../components/Button/Button.styles";
export { Checkbox } from "../components/Checkbox/Checkbox";
export * from "../components/Checkbox/Checkbox.styles";
export { DatePicker } from "../components/DateTimePicker/DatePicker/DatePicker";
export { TimePicker } from "../components/DateTimePicker/TimePicker/TimePicker";
export { DayPicker } from "../components/DayPicker/DayPicker";
// etc etc etc
Let me know if you experience any problems with babel, rollup, or have any questions about bundling/libraries.
When you bundle code with Webpack (or Parcel or Rollup) it creates one single file with all the code.
For performance reasons I do not want to all that code to be downloaded by the browser unless it is actually used
It's possible to have separate files generated for each component. Webpack has such ability by defining multiple entries and outputs. Let's say you have the following structure of a project
- my-cool-react-components
- src // Folder contains all source code
- index.js
- componentA.js
- componentB.js
- ...
- lib // Folder is generated when build
- index.js // Contains components all together
- componentA.js
- componentB.js
- ...
Webpack file would look something like this
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
componentA: './src/componentA.js',
componentB: './src/componentB.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'lib'),
},
};
More info on "code splitting" is here in Webpack docs
If the same repo contains lots of different components, what should be in main.js?
There is a single field in package.json file named main, it's good to put its value lib/index.js according to the project structure above. And in index.js file have all components exported. In case consumer wants to use single component it's reachable by simply doing
const componentX = require('my-cool-react-components/lib/componentX');
Am I right in thinking that I should not bundle the components? Should the bundling be left to the consumer of the components? Do I leave anything else to the consumer of the components? Do I just transpile the JSX and that's it?
Well, it's up to you. I've found that some React libraries are published in original way, others - are in bundled way. If you need some build process, then define it and export bundled version.
Hope, all your questions are answered :)
You can split your components like lodash is doing for their methods.
What you probably have is separate components that you could allow importing separately or through the main component.
Then the consumer could import the whole package
import {MyComponent} from 'my-components';
or its individual parts
import MyComponent from 'my-components/my-component';
Consumers will create their own bundles based on the components they import. That should prevent your whole bundle being downloaded.
You should take a look at Bit, I think this is a good solution to share, reuse and visualize components.
It is very easy to setup. You can install your bit library or just a component with:
npm i #bit/bit.your-library.components.buttons
Then you can import the component in your app with:
import Button3 from '#bit/bit.your-library.components.buttons';
The good part is that you don't have to worry about configuring Webpack and all that jazz. Bit even supports the versioning of your components. This example shows a title-list react component so you can take a look if this meets your requirements or not
There is a configuration in webpack to create chunk files. To start with it will create the main bundle into multiple chunks and get it loaded as when required. if your project has well structured modules, it will not load any code which is not required.

How to add sourcemap in React Native for Production?

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

Resources