My Problem
I have a ReactJS website that parses a JSON file (stored in the src folder) to render content. I recently attempted to use a YAML file instead because of YAML's richer abilities (supporting Markdown, for example).
Because React must load and parse YAML asynchronously, this change complicated my codebase and degraded my app's performance (e.g., content flickers).
My Plan
Instead of rewriting my components and slowing down my app, I've written a script to convert my YAML file into a JSON file. This way, I can have the best of both worlds: the richer abilities of YAML and the simple implementation of JSON.
My Question
What's the best way to make React run this script when the app is built? My web host rebuilds my app every time I push a change, so hopefully this can be activated via package.json or similar.
My Solution
I solved the problem using js-yaml. I even found a way to make it work with Fast Refresh.
./src/[PATH]/yaml-convert.js:
const yaml = require('js-yaml');
const fs = require('fs');
const args = process.argv.slice(2);
switch (args[0]) {
case 'watch':
watch();
break;
default:
convert();
}
function watch() {
fs.watch('./src/[PATH]/myData.yaml', function (event, filename) {
require('child_process').fork('./src/[PATH]/yaml-convert.js');
});
}
function convert() {
try {
const json = yaml.load(fs.readFileSync('./src/[PATH]/myData.yaml', 'utf8'));
fs.writeFileSync('./src/[PATH]/myData.json', JSON.stringify(json));
} catch (e) {
console.log(e);
}
}
package.json:
(note the -- watch argument and the single & in the dev command)
...
"scripts": {
"start": "serve -s build",
"dev": "npm run yaml-convert -- watch & react-scripts start",
"build": "npm run yaml-convert && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"yaml-convert": "node ./src/[PATH]/yaml-convert.js"
}
...
And to keep git diff tidy...
.gitattributes:
/src/[PATH]/myData.json -diff
Try creating an empty object that will store the converted data from the YAML, and use an if statement to check if the former object length is >0 then render what you want from the data (and if not, render a placeholder)
Related
Is there any way to fail the yarn build step in case any environment variable is missing. In the bitbucket pipeline script the build process is executing even if no env variable is set under the repository variables.
Yes, this can be possible using an approach in the React app. In the root directory create a JS file named as validate-env.js and add the below content in it (I'm using only these env variables in my app - change them according to yours)
require('dotenv').config();
if (!process.env.REACT_APP_WEB_SOCKET_URL) {
throw 'REACT_APP_WEB_SOCKET_URL undefined';
} else if (!process.env.REACT_APP_API_URL_PROD) {
throw 'REACT_APP_API_URL_PROD undefined';
} else if (!process.env.REACT_APP_NODE_ENV) {
throw 'REACT_APP_NODE_ENV undefined';
} else if (!process.env.REACT_APP_CATE_APP) {
throw 'REACT_APP_CATERING_APP undefined';
} else if (!process.env.REACT_APP_FRESH_CHAT_TOKEN) {
throw 'REACT_APP_FRESH_CHAT_TOKEN undefined';
} else if (!process.env.REACT_APP_SENTRY_DSN_KEY) {
throw 'REACT_APP_SENTRY_DSN_KEY undefined';
} else {
console.log('required env set');
}
Make sure to install a dev dependency as yarn add dotenv -D
Now under the package.json file > script section add this line
"validate-env": "node ./validate-env",
and update the build script as (if you are using craco)
"build": "yarn validate-env && craco build",
So, whenever you will run yarn build. It will first check if all the env are present. IF anyone is missing it will fail the build process.
I'm trying to understand how SonarQube is handling Code Coverage for ReactJS.
I have created basic Jest test which is executed by a script set in my package.json:
"test": "react-scripts test --env=jsdom --coverage --testResultsProcessor jest-sonar-reporter",
and has set coverage and reporting as
"jestSonar": {
"reportPath": "reports",
"reportFile": "test-report.xml",
"indent": 4
}
Test will pass, coverage folder and report is created as expected. Then I try to run sonar-scanner which has a config as follow:
const sonarqubeScanner = require('sonarqube-scanner');
sonarqubeScanner({
serverUrl: 'https://sonarqube.myhost.com/',
options : {
'sonar.sources': '.',
'sonar.exclusions' : 'src/**/*.bak.*, src/**/*.bak, src/**/*.orig, **/*.test.*',
'sonar.inclusions' : 'src/**',
'sonar.tests': "./src/__tests__",
"sonar.test.inclusions": "./src/__tests__/**/*.test.js, ./src/__tests__/*.test.js",
'sonar.javascript.lcov.reportPaths' : 'coverage/lcov.info',
"sonar.testExecutionReportPaths": "reports/test-report.xml",
"sonar.coverage.jacoco.xmlReportPaths" : "reports/test-report.xml"
}
}, () => {});
SonarQube scan will return SUCCESS but when I will go to the SonarQube dashboard for my project I do not see any updates for the Code Coverage neither Unit Test reported.
Any clue what is going on with it?
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
I'm trying to set an external config in my webpack according to the NODE_ENV.
Here's my script code from package.json:
"scripts": {
"build": "set NODE_ENV=production && webpack --mode production",
"start": "webpack-dev-server --hot --mode development"
},
Then in my webpack.config.js I have the following code:
externals: {
'Config': JSON.stringify(process.env.NODE_ENV === 'production' ? {
apiUrl: "."
} : {
apiUrl: "http://localhost:3000"
})
},
Somehow this doesn't work. I always get the "false" (localhost) as value. I'm 100 % sure process.env.NODE_ENV is set since first of all, I log it at the beginning of the file (console.log(process.env.NODE_ENV)) and it gives me "production" as answer.
Second, I tried to create another config like:
externals: {
'Test': {"mytest": process.env.NODE_ENV } ...
and this sets mytest to "production" ....
I have no idea why this doesn't work, I copied the code from stackoverflow (How to store Configuration file and read it using React).
I tried everything... I wrapped around the JSON.stringify around other parts, I tried to debuff as good as I can. Its now the 4th hour I spend on this problem and I think its a good moment to ask here for some advise.
If you can help me solve this problem, I would be really thankful!
Have a nice day.
I used creat-react-app to initialize some code which I want to share between native and web. In my package.json I have two separate commands for starting for each platform using react-scripts-ts and react-native-scripts-ts:
package.json
...,
"scripts": {
"tsc": "tsc",
"clean": "rimraf artifacts",
"build": "npm run clean && npm run tsc --",
"start-web": "npm run build && react-scripts-ts start",
"start-native": "npm run build && react-native-scripts start",
},
...
(a detailed description on how to do this can be found here https://medium.com/#yannickdot/write-once-run-anywhere-with-create-react-native-app-and-react-native-web-ad40db63eed0)
This great and I can use react-native components on both platforms. The problem I have is when I try to use external packages such as react-routing.
I include both react-router-native and react-router-dom in my package.json. I am trying to achieve what is described in this article (https://medium.com/#yannickdot/hi-jared-2650fbb2eda1) but using typescript not JS, giving me:
routing.native.tsx
export {
NativeRouter as Router, // Rename
Switch,
Route,
Link
} from 'react-router-native'
routing.web.tsx
export {
BrowserRouter as Router,
Switch,
Route,
Link
} from 'react-router-dom'
However, contrary as described in the article, when using typescript, it is not automatically recognized which file should be included. I get the simple error:
src/App.tsx:10:26 - error TS2307: Cannot find module './routing'.
10 import Router from "./routing";
Which makes sense because when I look at the output of the compiler, no module routing exists:
artifacts
| App.js
| routing
| routing.native.js
| routing.web.js
How can I tell the typescript compiler to include all *.native.tsx files when running the start-native command and all *.web.tsx files when running the start-web commmand?
Ideally, this should be possible at compile-time, passing additional parameters into the typescript compiler, which override the tsconfig.json. Example:
tsc --exclude="./**/*.native.tsx"
I know this can be done with a hacked solution, e.g. by writing a script to copy the entire source, deleting all unwanted files, keeping the correct ones, and compiling that copied source folder, but I want to know if there is a more neat way to do this.
Thanks in advance!
A possible solution without using external tools :
1. Create a function to check the platform running
see this question on Stackoverflow
export default function getPlatform(): string {
if (typeof document != 'undefined') {
// I'm on the web!
return 'web';
}
else if (typeof navigator != 'undefined' && navigator.product == 'ReactNative') {
// I'm in react-native
return 'native';
}
else {
// I'm in node js
return 'node';
}
}
2. Create routing/index.ts
import getPlatfrom from '../getPlatform';
const platform = getPlatfrom();
const routing = platform === 'web' ? require('./routing.web') : require('./routing.native');
export const {Router, Switch, Route, Link} = routing;
3. Use the routing
import { Route } from './routing/index';
You may add an interface IRouting and some type casting in routing/index, so that you don't lose type safety and autocompletion ...