Preact SSR hot reload/rebuild - javascript

I am trying to add automatic browser refresh to the dev environment for my preact ssr build.
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start:client": "webpack -w",
"start:server": "babel-node server.js",
"dev": "yarn run start:client & yarn run start:server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#babel/cli": "^7.11.6",
"#babel/core": "^7.11.6",
"#babel/node": "^7.10.5",
"#babel/preset-env": "^7.11.5",
"babel-loader": "^8.1.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
},
"dependencies": {
"express": "^4.17.1",
"preact": "^10.5.2",
"preact-render-to-string": "^5.1.10",
"preact-router": "^3.2.1"
}
webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.join(__dirname, "dist"),
filename: "app.js"
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ['#babel/preset-env']
}
}
}
]
},
mode: 'development'
};
server.js
const express = require('express');
const path = require('path');
const render = require('preact-render-to-string');
const { h } = require('preact');
const { App } = require('./src/App');
const app = express();
const HTMLShell = (html) => `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>preact ssr</title>
</head>
<body>
<div id="app">${html}</div>
<script src="./app.js"></script>
</body>
</html>
`;
app.use(express.static(path.join(__dirname, 'dist')));
app.get('*', (req, res) => {
const html = render(<App />);
res.send(HTMLShell(html));
});
app.listen(3001);
When I run yarn dev, the webpack is correctly watching for any changes and rebuilding. I have to manually refresh the browser to see the effects, is there a way to automate this?

It doesn't look like you have webpack-dev-server as part of this project, which is responsible for handling the live reloading of your browser as changes occur. Start by adding that to your project:
yarn add webpack-dev-server --dev
Then, replace the current scripts in your package.json with the following:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start:client": "webpack-dev-server",
"start:server": "babel-node server.js",
"dev": "yarn run start:client & yarn run start:server"
},
Running yarn dev should give you live reload with recompiling after making that change.

Related

Webpack Getting Started Project Not Running On Chrome, Fine on Firefox

I'm trying to run through the "Getting Started" section in Webpack's documentation here. I'm at the section before loading images - the .html file loads as expected ("Hello webpack" in red text) when I open it on Mozilla Firefox, but I get an Uncaught SyntaxError: Invalid or unexpected token (at bundle.js:2:8083) when I open it with Chrome. I'll include my index.js, dist/index.html, package.json, and webpack.config.js below:
index.js:
import _ from 'lodash';
import './style.css';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.classList.add('hello');
return element;
}
document.body.appendChild(component());
dist/index.html
<!doctype html>
<html>
<head>
<title>Asset Management</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
package.json
{
{
"name": "Webpack Demo",
"version": "0.0.0",
"license": "MIT",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"dependencies": {
"lodash": "^4.17.21",
"three": "^0.140.0"
},
"devDependencies": {
"css-loader": "^6.7.1",
"style-loader": "^3.3.1",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2"
}
}
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
Please let me know if you can figure out what I'm doing wrong! Thanks

Bundling amazon-sp-api with webpack

I'm, trying to generate a bundle from a javascript to get some requests from Amazon Selling Partner API. Actually, I have a mockup of a getOrders request which works in Node.js by using the
[amazon-sp-api client package] (https://www.npmjs.com/package/amazon-sp-api#setting-credentials-from-environment-variables).
As I need to get the script working in Oracle Netsuite, I would need to copy that script to the Netsuite account as a unique-file bundle.
So, I have generated the bundle I need but it launches a TypeError when trying to execute it as a unique file with Node. Indeed, the error header looks like this:
...readdirSync(__dirname + '/resources').reduce((eps, ep) => {
^
TypeError: readdirSync is not a function
It appears to be some issue about my webpack.config.js as I'm using a basic config and it probably requires specific details to lead with the dependencies.
There is a dependency I use in my script, which at once use third-party dependencies:
const SellingPartnerAPI = require('amazon-sp-api');
My webpack.config.js looks like this:
const path = require('path');
//const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
//This property defines where the application starts
entry:'./app.js',
//This property defines the file path and the file name which will be used for deploying the bundled file
output:{
path: path.join(__dirname, '/dist'),
filename: 'get_orders.js'
},
//Setup loaders
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
},
// Setup plugin to use a HTML file for serving bundled js files
plugins: [
/* new HtmlWebpackPlugin({
template: './src/index.html'
})*/
]
}
And the package.json is this:
{
"name": "orders-importer-bundle",
"version": "1.0.0",
"description": "Project to create a test blundle to import orders from Amazon to Netsuite using SP-API and SuiteScript.",
"main": "app.js",
"scripts": {
"pack": "webpack-dev-server --mode development --open --hot",
"dev": "webpack --mode development",
"build": "webpack --mode production",
"start": "node ./app.js"
},
"repository": {
"type": "git",
"url": "main"
},
"keywords": [
"webpack",
"bundle",
"netsuite",
"amazon",
"sp-api"
],
"author": "Roberto Carlos Rodriguez",
"license": "ISC",
"dependencies": {
"amazon-sp-api": "^0.7.1"
},
"devDependencies": {
"#babel/cli": "^7.17.6",
"#babel/core": "^7.17.7",
"#babel/preset-env": "^7.16.11",
"#babel/preset-react": "^7.16.7",
"babel-core": "^6.26.3",
"babel-loader": "^8.2.3",
"html-webpack-plugin": "^5.5.0",
"babel-preset-env": "^1.7.0",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
}
}

Exposing variables from a library with webpack

I don't seem to understand how webpack works. I would like to create a plain javascript library with some reusable components that I can use in other applications and in script tags in the html. So I tried to make a very simple library that exposes one variable containing a string. Should be simple I thought, but can't seem to get it to work.
My webpack.config.js:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
library: {
name: 'mypack',
type: 'umd',
},
},
devtool: 'source-map',
devServer: {
watchContentBase: true,
contentBase: path.resolve(__dirname, 'dist'),
port: 9000
},
module: {
rules: [
{
test:/\.css$/,
use:['style-loader', 'css-loader']
}
]
},
}
My package.json:
{
"name": "mypack",
"version": "0.0.1",
"main": "./src/app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"start": "webpack ./src/app.js -d eval --watch --mode development",
"dev": "npx webpack serve"
},
"author": "me",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.0.0",
"webpack": "^5.44.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"style-loader": "^3.2.1"
}
}
My src/app.js
let myvar = "test";
export {myvar};
My dist/index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8"><title>test</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="main.js"></script>
</head>
<body>
Test
<script>
console.log(mypack.myvar);
</script>
</body>
</html>
mypack.myvar gives an 'undefined' in the console. mypack seem to be an empty object {}.
How can I access myvar in my package? What am I doing wrong?
Of course, this is only a dummy, in reality I would like to expose objects from the package.
In the end it seems to be a problem with the webppack-dev-server.
If I comment out the line 'contentBase: path.resolve(_dirname, 'dist'),' from the webpack.config.js, copy the index.html to the root of my package and change the script tag source to "dist/index_bundle.js" then it works.
Not sure what is going on there, the script seems to load just fine in the situation above (in the sourceview in the browser, I can click the link and I see the generated javascript) but doesn't seem to be working at all. But that's another question.

Webpack not building the most recent changes

I am working on a fairly simple project from https://medium.com/ethereum-developers/the-ultimate-end-to-end-tutorial-to-create-and-deploy-a-fully-descentralized-dapp-in-ethereum-18f0cf6d7e0e
Since the tutorial doesn't focus on the frontend part(webpack and babel and other things), I picked up these steps from different places.
Now I was trying to build the front using webpack and http-server, but I realized that it is not updating with the changes that I am making to the file.
webpack.config.js
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src/js', 'index.js'), // Our frontend will be inside the src folder
output: {
path: path.join(__dirname, 'dist'),
filename: 'build.js' // The final file will be created in dist/build.js
},
module: {
rules: [{
test: /\.css$/, // To load the css in react
use: ['style-loader', 'css-loader'],
include: /src/
}, {
test: /\.jsx?$/, // To load the js and jsx files
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}]
}
}
package.json
{
"name": "test-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#babel/core": "^7.10.2",
"#babel/preset-env": "^7.10.2",
"#babel/preset-react": "^7.10.1",
"babel-loader": "^8.1.0",
"css-loader": "^3.5.3",
"json-loader": "^0.5.7",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"style-loader": "^1.2.1",
"web3": "^0.20.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11"
},
"directories": {
"test": "test"
},
"dependencies": {},
"description": ""
}
I build it using
npx webpack --config webpack.config.js
and then serve it
http-server dist/
How do I fix this? And is this even the right way to do it?
Thanks.
U have already webpack-cli installed in your dependencies so u dont have to add config in command:
First Add Webpack Script in your Package.json:
"scripts": {
"watch": "webpack --watch",
},
When u run npm run watch --watch webpack will continue to watch for changes in any of the resolved files.
And for Server I recommend you webpack-dev-server
npm i webpack-dev-server
can be used to quickly develop an application
module.exports = {
//...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000
}
};
And add it to your npm script
"scripts": {
"watch": "webpack --watch",
"start": "webpack-dev-server --hot --open",
}
Now we can run npm start from the command line and we will see our browser automatically loading up our page. If you now change any of the source files and save them, the web server will automatically reload after the code has been compiled.
Advise: you must add html file in dist or plugins for webpack HtmlWebpackPlugin

page doesn't load on refresh - react-router-dom

I have basic react app with the following pages: Home, Profile, contact, and experience. I have set up the routes for each page but when I go to a different page other than home, it renders but when I refresh page, the page doesn't load.
I noticed if I add the # before the page name, for example http://localhost:1234/#/profile the page renders. So i'm confused why I need to the # which I don't want and I'm using react-router-dom, so there is no need for #.
I added the historyApiFallback to my webpack.config but that doesn't work. Can anyone help me with this? I'm new to react and I want to learn as much as possible. Your help will be appreciated!
App.jsx
import React, { Component } from "react";
import Navbar from "./components/navbar";
import Intro from "./components/introPage";
import Experience from "./components/experiencePage";
import Profile from "./components/profilePage";
import Contact from "./components/contactPage";
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
class App extends Component {
render(){
return (
<Router>
<div className="pageSections">
<Navbar />
<div className="navContent">
<Route exact path="/" component={Intro}/>
<Route path="/experience" component={Experience}/>
<Route path="/profile" component={Profile}/>
<Route path="/contact" component={Contact}/>
</div>
</div>
</Router>
);
}
}
export default App;
navbar.jsx
import React, { Component } from "react";
import { Link } from "react-router-dom";
class Navbar extends Component {
render(){
return (
<div className="navFrame">
<Link to="/">
<div className="topNav"><div className="navBar"><h3>Marteen</h3></div></div>
</Link>
<Link to="/profile">
<div className="rightNav"><div className="navBar"><h3>Profile</h3></div></div>
</Link>
<Link to="/experience">
<div className="bottomNav"><div className="navBar"><h3>Experience</h3></div></div>
</Link>
<Link to="/contact">
<div className="leftNav"><div className="navBar"><h3>Contact</h3></div></div>
</Link>
</div>
);
}
}
export default Navbar;
webpack.config.js
const webpack = require('webpack');
const config = {
entry: __dirname + '/js/index.jsx',
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx', '.css']
},
module: {
rules: [
{
test: /\.jsx?/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.scss?/,
loader: 'style-loader!css-loader!sass-loader'
}
]
},
devServer: {
historyApiFallback: true,
contentBase: './',
hot: true
}
};
module.exports = config;
package.json
{
"main": "index.js",
"scripts": {
"build": "webpack -p --progress --config webpack.config.js",
"dev-build": "webpack --progress -d --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --progress -d --config webpack.config.js --watch",
"start": "npm run open",
"open": "concurrently \"http-server -a localhost -p 1234\" \"open http://localhost:1234\""
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"concurrently": "^3.6.1",
"css-loader": "^0.28.11",
"http-server": "^0.11.1",
"node-sass": "^4.7.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"sass-loader": "^6.0.7",
"style-loader": "^0.20.3",
"webpack": "^4.1.1",
"webpack-cli": "^2.0.12"
},
"dependencies": {
"npm": "^5.10.0"
}
}
Update
package.json
{
"name": "fullstack_profile",
"version": "1.0.0",
"description": "fullstack profile with flask and react",
"main": "index.js",
"scripts": {
"build": "webpack -p --progress --config webpack.config.js",
"dev-build": "webpack --progress -d --config webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack-dev-server --hot --progress --mode development",
"start": "npm run open",
"open": "concurrently \"http-server -a localhost -p 1234\" \"open http://localhost:1234\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/medev21/fullstack_profile.git"
},
"author": "martin",
"babel": {
"presets": [
"es2015",
"react"
]
},
"license": "ISC",
"bugs": {
"url": "https://github.com/medev21/fullstack_profile/issues"
},
"homepage": "https://github.com/medev21/fullstack_profile#readme",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"concurrently": "^3.6.1",
"css-loader": "^0.28.11",
"http-server": "^0.11.1",
"node-sass": "^4.7.2",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"sass-loader": "^6.0.7",
"style-loader": "^0.20.3",
"webpack": "^4.1.1",
"webpack-cli": "^2.0.12",
"webpack-dev-server": "^3.1.5"
},
"dependencies": {
"npm": "^5.10.0"
}
}
webpack.config
const webpack = require('webpack');
const config = {
entry: __dirname + '/js/index.jsx',
output: {
path: __dirname + '/dist',
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx', '.css']
},
module: {
rules: [
{
test: /\.jsx?/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.scss?/,
loader: 'style-loader!css-loader!sass-loader'
}
]
},
devServer: {
contentBase: __dirname + '/dist',
compress: false,
port: 1234,
historyApiFallback: {
index: 'index.html'
}
}
};
module.exports = config;
The reason why http://localhost:1234/#/profile works is because # doesn't reload the page. It behaves the same as an anchor tag in HTML where you stay on the same page but scroll to a specific section of it. For example, the below scrolls you to the portfolio section of the page.
Portfolio
If you omit the #, this is different. This tells your server to reload the page and visit that location. In your case http://localhost:1234/profile is unknown by the server as you have not specified it on the server side. In these cases, you need to either create the route or proxy the request when the route is not found.
As you are using react-router which is a client-side router, the same file needs to be served by the server and therefore you should go with the proxy option.
When using http-server the docs say you can add the -P or --proxy flag to specify it.
-P or --proxy Proxies all requests which can't be resolved locally to the given url. e.g.: -P http://someurl.com
In your case update the below in your package.json.
"open": "concurrently \"http-server -a localhost -p 1234 -P http://localhost:1234\" \"open http://localhost:1234\""
An alternative is to use webpack-dev-server for development. It'll improve your developers experience a lot as it supports hot reloading when a component changes.
install it using npm install --save-dev webpack-dev-server
Add the below to your webpack config.
devServer: {
contentBase: __dirname + '/dist',
compress: false,
port: 1234,
historyApiFallback: {
index: 'index.html' // assuming this is your entry point file that loads your bundle.
}
},
Then in your package.json add a script for it and run it using npm run watch
"watch": "webpack-dev-server --hot --progress --mode development",
NOTE: Make sure that in your dist folder index.html is present. If it isn't you will run into issues.
I've created a basic project on GitHub so you have a working example.
Solution to Express and at least to Heroku deployment
I want to share my solution that finally allowed the user to access every link in my React App.
After reading these proposed solutions and this helpful article, I could manage to make this works with this following code in my main server.js file:
// Serve static files such as images, CSS files, and JavaScript files for the React frontend app
app.use(express.static(path.join(__dirname, 'client/build')))
// This solves the "Not found" issue when loading an URL other than index.html.
app.get('/*', (req, res) => { //n3
res.sendFile(path.join(__dirname + '/client/build/index.html'), err => {
if (err) { res.status(500).send(err) }
})
})
Make sure to always point the path to build which is where all the files are loaded in the production environment.
After that, all urls are working well finally!

Categories

Resources