How to compress build files in create react app without ejecting? - javascript

I'm trying to figure out how to best optimize my build file and ran across the notion of compressing text files like js and css. From what I've come across every article either assumes you have access to the webpack config file or you've ejected from CRA. I don't want to.
So I added a post build script to my package.json file:
"scripts": {
"build": "npm run build --prefix client",
"prod-compress": "gzip client/build/static/js/*.js && gzip client/build/static/css/*.css",
which results in the /client/build/static/js and /client/build/static/css folders looking like this:
I then went into my app.js file and added the following code:
app.get('*.js', function(req, res, next) {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'text/javascript');
next();
});
app.get('*.css', function(req, res, next) {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'text/css');
next();
});
If I understand what's happening correctly, the f/e /client/public/index.html file will still reference a regular .js file. However, when the file is requested from the server, it will respond with the .js.gz file.
However, when I compress the files the entire site goes blank, like it can't find anything to serve up.

If you don't mind adding a new dependency, I would recommend using express-static-gzip which will automatically serve your compressed files:
const express = require("express");
const expressStaticGzip = require("express-static-gzip");
const app = express();
const buildPath = path.join(__dirname, '..', 'build', 'static');
app.use("/", expressStaticGzip(buildPath);
You also have the choice to add other types of compression like brotli by passing an options object:
const buildPath = path.join(__dirname, '..', 'build', 'static');
app.use(
'/',
expressStaticGzip(buildPath, {
enableBrotli: true,
orderPreference: ['br', 'gz']
})
);
Brotli gives you even more optimized files than gzip but it's not supported by all browsers. Thankfully, express-static-gzip automatically picks the correct file to send based on the Accept-Encoding/Content-Encoding header the user's browser sends to it.
If you want to use brotli, I recommend taking a look at compress-create-react-app. It's specifically made for React apps but should work with any files.
Disclaimer: I'm the author of compress-create-react-app

Related

Express server don't search static files in Angular 12 app

Background
I am migrating an Angular app in GKE cluster. The base docker image that I must use(company policy) does not have any options to install any new softwares like shell, Angular cli command ng etc. The base docker image has only Node installed.
There is a shared base url, let's say, www.my-company.com, that everyone has to use for app deployment with a path added after the base url like www.my-company.com/my-angular-app/ - all the other Angular apps must be differentiated using the path of the app.
What I did
Since I can't run ng serve command in the base image, I added Express dependency in the package.json in Angular application and created an express server to route the traffic to Angular app.
I was following this youtube video to configure the application - https://www.youtube.com/watch?v=sTbQphoYbK0&t=303s. The problem I am facing is to how I load the the static files in the application.
If I define absolute path inside sendFile method of server.js file, although the application is working, but in future, if I need to add any other files in the application, I have to create another route in server.js file.
I don't know how Express can search a file automatically from the static folder(and sub folders) and return only that file when needed. I defined a static folder too, but seems like it is not working.
Following is my server.js code
==============================
const express = require('express');
const http = require('http');
const path = require('path');
const port = 8080;
const contextPath = '/my-angular-app';
const router = express.Router();
const app = express();
app.use(contextPath, router);
app.listen(port, ()=> {
console.log("Listening on port: ", port);
});
app.use(express.static(__dirname + '/dist/testapp/'));
router.get('/', function(req, res) {
// to get index.html file
res.sendFile(path.resolve(__dirname + '/dist/testapp/index.html'));
});
router.get('/*', function(req, res) {
let path = __dirname +'/dist/testapp/' + req.path
console.log('full path: ', path);
// To return static files based on incoming request, I am facing problem here(I think)
res.sendFile(path);
});
==============================
I want Express will send any files based on file name in the request. It should also take care of nested directories in the /dist/testapp/ directory
/dist/testapp/ -> This is the directory where Angular generates code for my app after I execute ng build command
WEBAPP.get("/admin/script.js", (req, res) => {
console.log(req.path);
if (req.session.username !== "Admin") return res.render("error");
res.sendFile(__dirname + "/admin/admin.js")
});
WEBAPP.get("/admin", (req, res) => {
if (!req.session.loggedin) return res.render("error");
if (req.session.username !== "Admin") return res.render("error",);
res.render("admin", {
csrfToken: req.csrfToken(),
title: "ADMIN PORTAL",
username: req.session.username,
nav_avatar: GetImageURL(req.session.avatar, "small")
});
});
There's no need to publically share /admin/script.js in my case but if a user requests this URL say example.com/admin/script.js a check for username equaling "Admin" if all is okay we sendFile.
I would maybe assume that you're not properly targeting your static files. Perhaps console.log the target.

Static files not reading the .env file in a node js serve

I have a react app that I was able to build into static files which I then added to my node js app. after installing dotenv and adding require('dotenv').config() to server.js in the node server application. The following piece of code I had in the react application does not seem to be working now although the static files are being rendered in the browser just fine.
let node_server_url;
//TODO
// set the NODE_ENV on the .env file.
// create the .env file on the root of the project and add a NODE_ENV on the file
// e.g NODE_ENV=development or NODE_ENV=production
let urlConfig = (process.env.NODE_ENV === "production") ? {
urlBackend: `https://mybluebookloanuat.bayportbotswana.com`,
} : {
// urlBackend: `http://10.16.32.22:5000`,
urlBackend: `http://localhost:5003`,
}
// eslint-disable-next-line
export default node_server_url = urlConfig.urlBackend;
server.js:
require('dotenv').config()
...
const app = express();
// for parsing request body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// cross origin access to allow backend to communicate with frontend
app.use(cors());
const db = require("./app/models/index");
// to drop existing database and resync the database
db.sequelize.sync({force: false})
require("./app/routes/loan_application.routes")(app);
app.use(express.static(path.resolve(__dirname, 'assets')))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'assets', 'index.html'));
});
I want to be able to just set up in the .env file the address for development and for production in the node server app. Can someone help me?

How to send web-project to my localserver?

I don't quite understand how to link my web application and server. Tried to use
res.sendFile(path.resolve('./public/index.html'));
But it doesn't connect components written in vue.js
In vue.config.js (make one in your client src folder if you haven't already), add the target directory for the build:
const path = require("path");
module.exports = {
outputDir: path.resolve(__dirname, "path/to/server/directory/public")
};
Now when you run npm run build the static files will be bundled there.
Then, all you need to do is point your server to that folder.
If you're using Node/Express as a backend:
// Handle production
if (process.env.NODE_ENV === 'production') {
// Static folder
app.use(express.static(__dirname + '/public/'));
}
Also, if your Vue app is an SPA, add this inside the if block, after app.use(express.static(__dirname + '/public/'));, to handle routing:
// Handle SPA
app.get(/.*/, (req, res) => res.sendFile(__dirname + '/public/index.html'));

middleware on res.render() when using a lot of routes

I've created a webpage to use it locally. I have a ton of routes like the ones shown below -- 31 .ejs files and 3 .html files. (They are all in the same "views" folder).
//app.js - using node and express
app.get('/page1', function(req, res){
res.render('page1');
});
app.get('/page2', function(req, res){
res.sendFile('views/page2.html', { root: __dirname });
});
I use an app.get for each and every one of these files. I've had a feeling it wasn't DRY code, and so now I'm trying to figure out a more elegant and optimal way to achieve the same result.
I know that many res.sendFile(); could be replaced with a single express.static() middleware statement. I usually use express.static() on a "public" folder which I use to save all my css files -- like this app.use(express.static(path.join(__dirname, 'public')));. But I still don't see how I could use this to simplify all my res.sendFile().
As for the many res.render(); routes, I know that if I don't pass any customized data I could probably replace them with a single middleware that handles either a whole directory of template files (and their corresponding routes) or a list of files. I just don't know how I would do that.
Any help is very much appreciated, thanks!!
[UPDATE]
richie node_modules public
css files, images, etc views
partials
all partial files programmingPublic
all ejs files from a same topic other files (html & other ejs) appjs
packagejson package-lockjson
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
// Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// engine
app.set("view engine", "ejs");
// Set static path
app.use(express.static(path.join(__dirname, 'public')));
const fs = require('fs');
function renderStatic(dir) {
return function(req, res, next) {
let target = path.join(dir, req.path);
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "views/programmingPublic")));
Below is the format of my side-menu: (all these files are inside "programmingPublic" folder)
Programming
<li>C</li>
<li>C++</li>
<li>Python</li>
<li>JavaScript</li>
<li>PHP</li>
If you have a bunch of pages that need to call res.render(), but aren't passing custom options to each render, then you could isolate all those templates in their own directory and then use some middleware like this:
const path = require('path');
const fs = require('fs');
function renderStatic(dir, options) {
const regex = /^\.|\.\.|\/\.|\\\./;
options = options || {};
return function(req, res, next) {
let target = path.join(dir, req.path);
if (options.ext && !path.extname(target)) {
target = target + options.ext;
}
// don't allow leading dot or double dot anywhere in the path
if (regex.test(target)) {
next();
return;
}
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "renderPublic"), {ext: ".ejs"}));
Note, you must isolate these template files in their own directory so that other files are not found there.
For safety completeness, this code also needs to filter out . and .. items in the path like express.static() does to prevent an attacker from going up your directory hierarchy to get access to other files than those in the render static directory.
Then, for the routes you are using res.sendFile() and no other logic, just isolate those HTML files in their own directory and point express.static() at that directory. Then, the express.static() middleware will find a matching HTML file in that directory and do res.sendFile() for you automatically, exactly the same as it does for your CSS files.

Basic webserver with node.js and express for serving html file and assets

I'm making some frontend experiments and I'd like to have a very basic webserver to quickly start a project and serve the files (one index.html file + some css/js/img files). So I'm trying to make something with node.js and express, I played with both already, but I don't want to use a render engine this time since I'll have only a single static file, with this code I get the html file but not the assets (error 404):
var express = require('express'),
app = express.createServer();
app.configure(function(){
app.use(express.static(__dirname + '/static'));
});
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
app.listen(3000);
Is there a simple way to do it (in one file if possible) or Express requires the use of a view and render engine ?
I came across this because I have a similar situation. I don't need or like templates. Anything you put in the public/ directory under express gets served as static content (Just like Apache). So I placed my index.html there and used sendfile to handle requests with no file (eg: GET http://mysite/):
app.get('/', function(req,res) {
res.sendfile('public/index.html');
});
Following code worked for me.
var express = require('express'),
app = express(),
http = require('http'),
httpServer = http.Server(app);
app.use(express.static(__dirname + '/folder_containing_assets_OR_scripts'));
app.get('/', function(req, res) {
res.sendfile(__dirname + '/index.html');
});
app.listen(3000);
this loads page with assets
You could use a solution like this in node.js (link no longer works), as I've blogged about before.
The summarise, install connect with npm install connect.
Then paste this code into a file called server.js in the same folder as your HTML/CSS/JS files.
var util = require('util'),
connect = require('connect'),
port = 1337;
connect.createServer(connect.static(__dirname)).listen(port);
util.puts('Listening on ' + port + '...');
util.puts('Press Ctrl + C to stop.');
Now navigate to that folder in your terminal and run node server.js, this will give you a temporary web server at http://localhost:1337
Thank you to original posters, but their answers are a bit outdated now. It's very, very simple to do. A basic setup looks like this:
const express = require("express");
const app = express();
const dir = `${__dirname}/public/`;
app.get("/", (req, res) => {
res.sendFile(dir + "index.html");
});
app.get("/contact", (req, res) => {
res.sendFile(dir + "contact.html");
});
// Serve a 404 page on all other accessed routes, or redirect to specific page
app.get("*", (req, res) => {
// res.sendFile(dir + "404.html");
// res.redirect("/");
});
app.listen(3000);
The above example is if you want to serve individual HTML files. If you were serving a single page JS app, this would work.
const express = require("express");
const app = express();
const dir = `${__dirname}/public/`;
app.get("*", (req, res) => {
res.sendFile(dir + "index.html");
});
app.listen(3000);
If you need to serve other static assets from within a folder, you can add something like this before you start defining the routes:
app.use(express.static('public'))
Let's say you have a js folder inside public like: public/js. You could include any of those files inside of your html files using relative paths. For example, let's say /contact needs a contact.js file. In your contact.html file, you can include the script as easy as:
<script src="./js/contact.js"></script>
Building off of that example, you can do the same with css, images etc.
<img src="./images/rofl-waffle.png" />
<link rel="stylesheet" href="./css/o-rly-owl.css" />
Hope this helps everyone from the future out.

Categories

Resources