How to Handle Multiple Environments in a React App with Azeru DevOps - javascript

Devs out there,
I am having a little issue while trying to setup multiple environment in my react project using Azure DevOps. There is not much info out there on how you can get this done and I can see to get it right.
I want to have the:
Development
Production
At the moment I have it implement as such on my React app:
src > service > JS instance.js > instance
import axios from 'axios'
const instance = axios.create({
baseURL: 'https://xxx-test.azurewebsites.net/api',
// baseURL: 'https://localhost:5001/api',
})
export default instance
My script looks like this, and I have not changed to the script under package.json:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},

Use .env file mechanism:
Add a .env file with:
REACT_APP_API_URL=https://localhost:5001/api
And modify your code:
import axios from 'axios'
import config from './config'
const instance = axios.create({
baseURL: process.env.REACT_APP_API_URL
})
export default instance
Now, in the pipeline, you need to set REACT_APP_API_URL env variable differently depending on your target environment. You can use pipeline variables for that (because they are also exposed as env variables). In yaml, it could look like this:
stages:
- stage: test
variables:
REACT_APP_API_URL: https://xxx-test.azurewebsites.net/api
jobs:
- job: test_job
steps:
- script: npm run build
# then deploy the app however you want
- stage: prod
variables:
REACT_APP_API_URL: https://xxx-prod.azurewebsites.net/api
jobs:
- job: prod_job
steps:
- script: npm run build
# then deploy the app however you want

Related

Executing a JavaScript file when running ng build

I would like to run a JavaScript file in my Angular application every time I run ng build. To be more precise, I want this file to be executed before the build process so that the changes that it makes are present in the build.
Its a simple script that reads the app version and its dependencies and write them to an object.
The file is called pre-build.js and I have tried configuring the build command in package.json as follows, however I can see that the script was not executed:
{
...
...,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "node pre-build.js && ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
...,
...,
}
The path of the script is ./pre-build.js.
I assume that I have to change more configurations in order to achieve this but I am not able to find out where. Any leads will be appreciated.
Edit:
This is the content of pre-build.js:
const path = require('path');
const fs = require('fs');
const appVersion = require('./package.json').version;
const appDeps = JSON.stringify(require('./package.json').dependencies);
const versionFilePath = path.join(__dirname + '/src/environments/version.ts');
const src = `export const version = '${appVersion}';\nexport const buildTime = '${new Date()}';\nexport const deps = ${appDeps};`;
// ensure version module pulls value from package.json
fs.writeFile(versionFilePath, src, (err) => {
if (err) {
console.log(err);
}
});
When I run node pre-build.js in the terminal, the code works fine and updates the version.ts file. But i want to somehow automatically execute this command every time i run ng build. Which so far i was not able to do so.
Edit
The correct answer to this problem is that you shouldn't run ng build but should run npm run build since you want to execute the script. When you do ng build this would only trigger the build for angular and wouldn't update your version file indeed.
Below is an example of your exact same code when doing npm run build, so make sure to update how you build.
Give it a try and let me know if this is still an issue.
Old answer
You can create a ".sh" script to other execute everything you need. This might be helpful later on to add more pre or post build commands
Here is an example
package.json
"scripts": {
"build:angular": "ng build",
"build": ./build.sh
}
build.sh
#!/bin/bash
node ./pre-build.js
npm run build:angular
Make sure that pre-build is executable so is the build.sh (chmod https://askubuntu.com/questions/229589/how-to-make-a-file-e-g-a-sh-script-executable-so-it-can-be-run-from-a-termi )
Try like this:
node ./pre-build.js && ng build

import web3 into react js getting BREAKING CHANGE: webpack < 5 used to incl

I am having problems with importing web3 into reactjs. To replicate my problem, initiallize a new react app as so
npx create-react-app my-app
cd my-app
then open terminal in this location. Write:
npm install web3
npm install
in the App,js file add the following line
import Web3 from "web3";
I got the error after I do npm start then I got the unsolved error which is
Module not found: Error: Can't resolve 'stream'
Module not found: Error: Can't resolve 'crypto'
I tried finding a solution online, in particular I tried each of
How to Polyfill node core modules in webpack 5
https://www.youtube.com/watch?v=u1PPNIBvQjk
Importing web3 causing a problem in react js
https://github.com/facebook/create-react-app/issues/11756#issuecomment-1001162736
https://namespaceit.com/blog/how-fix-breaking-change-webpack-5-used-to-include-polyfills-for-nodejs-core-modules-by-default-error
How to create React App including Web3 using create-react-app? I am getting Module not found Error. BREAKING CHANGE: webpack < 5 used
None seem to work with me. Is there any advice on how to solve this problem?
Thank you!
This is my solution as of Feb 2, 2022. This might change at a future date.
After initiating a React app as so
npx create-react-app my-app
cd my-app
you will need to install a few packages:
npm i web3, react-app-rewired, url, assert, buffer, crypto-browserify, stream-http, https-browserify, stream-browserify, os-browserify
Then you open your favourite code editor in my case it is MS VS Code editor as so on your terminal
code .
Create a JS file in the Root directory as config-overrides.js
Copy and paste the code available here
const webpack = require('webpack');
module.exports = function override(config, env) {
//do stuff with the webpack config...
config.resolve.fallback = {
url: require.resolve('url'),
assert: require.resolve('assert'),
crypto: require.resolve('crypto-browserify'),
http: require.resolve('stream-http'),
https: require.resolve('https-browserify'),
os: require.resolve('os-browserify/browser'),
buffer: require.resolve('buffer'),
stream: require.resolve('stream-browserify'),
};
config.plugins.push(
new webpack.ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer'],
}),
);
return config;
}
Open the package.json changed the scripts commands to this:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
This has solved the problem for me!
I am following simple steps to overcome this issue.
After you import web3 to the client app folder, uninstall react-scripts#5.0.0
then reinstall react-scripts but a lower version of 4.0.3
npm install react-scripts#4.0.3
i have not needed any other clumsy steps so far...Give it a try..
My Solution as of Aug 22, 2022
import the web3.min.js (from CDN or serve it locally) by adding the following to your public/index.html
<script src="web3.min.js"></script>
And use it in your component (TS) as :
const {Web3} = (window as any);
const web3 = new Web3(Web3.givenProvider);
const contractInstance = new web3.eth.Contract(contract.ABI, contract.address);
Try installing the missing modules using
npm i stream crypto

Save the data in the UI of react in the form of file

I am reading the data from an API and downloading a file. I want to save the same file in public folder of react application.
this.state = {
fileDownloadUrl: null,
fileName: '',
};
below is the method.
downLoadFile(e) {
e.preventDefault();
axios
.get(
'http://localhost:8080/file_download/en'
)
.then((res) => {
var output = res.data;
const blob = new Blob([output]);
const fileDownloadUrl = URL.createObjectURL(blob);
this.setState({ fileDownloadUrl: fileDownloadUrl }, () => {
this.dofileDownload.click();
URL.revokeObjectURL(fileDownloadUrl);
this.setState({ fileDownloadUrl: '' });
});
});
}
code for downloading the file.
<a
className="hidden"
download={this.state.fileName + '.json'}
href={this.state.fileDownloadUrl}
ref={(e) => (this.dofileDownload = e)}
>
download it
</a>
Is there anyway I can modify the code so that file can be saved in the public folder of react application. later this file will be used for translation.
There's no way to automatically download a file and save it in a specific location on your hard disk from a frontend app running inside a browser. The users of your app will decide how the file is saved based on their browser settings.
If you're trying to achieve that kind of thing, then that's wrong and you should consider another approach.
It's not possible because this poses a security risk.
Most of the OS will defaults the download to download folder.
Pure browser-JavaScript is not be able to get information about the user's filesystem. The default download path might also contain sensible information, which is risky.
To sum up the question, the challenge is to fetch or otherwise produce certain static file assets for React to consume.
As other answers pointed out, it's impossible to do from React itself. I'd add, if it were possible, that would be inefficient, unsafe and insecure.
Architecturally, it's best to create a file-generating script and run it before the React application's build step.
I'm using such setup myself. For example, if you checked my package.json I've got scripts:
{
"scripts": {
"--------- BUILD ---------": "",
"build": "npm run ops && run-s build:*",
"build:remix": "remix build",
"build:css": "npm run generate:css",
"--------- DEV ---------": "",
"dev": "npm run ops && run-p dev:*",
"dev:remix": "cross-env NODE_ENV=development remix dev",
"dev:css": "npm run generate:css -- --watch",
"generate:css": "sass styles:app/styles app/components",
"format": "prettier --write .",
"--------- TEST ---------": "",
"start:mocks": "cross-env NODE_ENV=production node --require ./mocks --require dotenv/config ./build/server.js",
"test": "vitest run --coverage",
"test:e2e:dev": "start-server-and-test dev http://localhost:3000 \"cypress open\"",
"pretest:e2e:run": "npm run build",
"test:e2e:run": "cross-env PORT=8811 start-server-and-test start:mocks http://localhost:8811 \"cypress run\"",
"--------- LINT ---------": "",
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
"typecheck": "tsc -b && tsc -b cypress",
"validate": "run-p \"test -- --run\" lint typecheck test:e2e:run",
"--------- OPS ---------": "",
"ops": "run-p ops:*",
"ops:static": "node ./ops/extract-static-data.mjs",
"ops:search": "node ./ops/extract-search.mjs",
"ops:rss": "node ./ops/extract-rss.mjs"
}
}
notice how ops group of scripts is triggered before the React build step. I generate static fresh files:
lump of all h2 heading strings for aside to set currently active anchor
all MDX front matter lump of all articles for rendering MDX outside normal routes (article lists, search etc.)
the final HTML of RSS feed; I render HTML with all remark goodies such as syntax titles, syntax highlighting and typography
compile the search data file and index file (for warm starts) for fuse.js
I'd fetch locales this way too: npm-triggered script would fetch and atomically save a file straight into /public/.
It's done during build-time rather than runtime to simplify the setup and lower the server bill. I disagree with Mr. Dodds in this aspect; more so, runtime libraries such as mdx-bundler don't even support pure ESM, so actually, runtime-oriented approaches often don't even work. For example, if you take the same Mr. Dodds' blog, it doesn't have the current anchor tracking in single posts or site-wide search or automated typography processing.
PS. Interestingly, if you look at the static site generators, 11ty lets you fetch extra sources from the 11ty config itself, that's where you would fetch the locales of yours on 11ty.

How to automatically remove console.logs() in development?

The Problem
Trying to lint an old React application that has 30+ console.logs() throughout the code base. I setup a linter and code formatter, ESLint and Prettier respectively.
This setup works great and it shows me where the console.logs() are in the source. But, the --fix flag for ESLint does not remove the logs. Is there any way to automatically remove console.logs()? I've seen articles on how to do this via webpack to prevent the logs from going into production, however, I would like to remove these earlier in a pre-commit or pre-push hook for instance.
What I've Tried
I tried setting up a script using gulp-strip-debug. It fails with an assertion error. When I changed the src path from './src/**.js' to './**.js' I do not get the assertion error but nothing happens.
Learned about this from Jun711 blog. The "How to remove console log from your JavaScript files programmatically?" blog post.
Gulpfile
gulpfile.js: root of project directory
const gulp = require('gulp');
const stripDebug = require('gulp-strip-debug');
gulp.task(
'strip-debug',
() =>
gulp
.src('./src/**.js') // input file path
.pipe(stripDebug()) // execute gulp-strip-debug
.pipe(gulp.dest('./')) // output file path
);
Package.json
{
"name": "end-the-pandemic",
"version": "1.0.0",
"dependencies": {
"shelter-in-place": "11.5.20",
"stay-home": "11.5.21",
"who-is-still-voting-for-trump": "11.3.20",
"seriously-why": "2.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"lint": "eslint '*/**/*.js' --quiet --fix",
"clean": "gulp strip-debug",
"test": "react-scripts test",
"eject": "react-scripts eject",
},
...
Terminal
yarn clean
Output
AssertionError [ERR_ASSERTION]: Task function must be specified
Obviously, I could have done a search and deleted most of them manually with the time I spent writing this question but for future reference, is there anyway to do this with a command? I would like to put yarn clean in a pre-commit hook if possible.
You could make this trick it would be good in this case
if (env === 'production') {
console.log = function () {};
}
this will overwrite the real log function into an empty one
make sure you add it at the top of your react app
Another solution
there is a package called babel-plugin-transform-remove-console could help you
after installing it from npm
npm install babel-plugin-transform-remove-console
after installing create a .babelrc file in your project root dir
and add this line to it
{
"plugins": ["transform-remove-console"]
}

Connecting react front end and express backend

The dev build works fine, with my react app on port 3000 and server on port 8080. The front end is able to make requests to the backend and get the response.
But when deploying the production build, the application starts on port 5000 and the static files are rendered.But the front end can't access localhost:8080/api/:id because that port isn't 'on'.
Any help will be appreciated.
You can use dotenv to define custom config variables and cross-env package to define an environment for your react application. First install these packages.
yarn add dotenv && yarn add cross-env --dev
or
npm i dotenv && npm i cross-env --dev
You need create two separate .env config files, name them as .env.development and .env.production. Your env file may contain something like this:
.env.development
API_URL="localhost:8080"
.env.production
API_URL="localhost:5000"
Now on your react's main.js file(or what you have named), just import the dotenv package as follows:
// other imports here
require('dotenv').config({
path: process.env.NODE_ENV === 'production' ? '/path/to/.env.production' : '/path/to/.env.development'
})
Now, on your package.json, change the start and build script as such:
{
...
"scripts": {
"start": "cross-env NODE_ENV=development react-scripts start",
"build": "cross-env NODE_ENV=production react-scripts build",
...
}
...
}
Finally, wherever you have been using the API URL:
Example
axios.post(`${process.env.API_URL}/your/path`) // or any other way to join URL and path
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
Hope it helps to solve your issue.
From all your routes remove localhost:8080 and make direct request to /api/ because everything is running under one PORT which is 5000. You might need to re-build your front-end with changes made

Categories

Resources