Update
Okay, so I updated the Procfile to bundle my components together
Procfile
web: npm run build-client && node server/index.js
But now it takes such a long time for my app to load because bundling takes a long time. Is there a better way to do this? This is a horrible user experience
...
And if nothing else, is there a way to render a static page right away that says:
Blame Heroku, not me
====== Original Context =======
My application works correctly with heroku local & localhost:8080, my view fails to render on deployment heroku open. From the console, I get this error message:
app.bundle.js:1 Uncaught SyntaxError: Unexpected token <
This particular bundle contains my React components. I used a codesplit with webpack to load in dependencies at different moments because I'm using a vr framework (aframe/three.js) on the front end and react. I don't understand why this is occurring if it works just fine locally.
index.html
I have codesplit some JS modules by aframe/three components inside of index.bundle. And all of my react components are inside of app.bundle
<head>
<meta charset="utf-8">
<title>Faceoff!</title>
<script src="./commons.js"></script>
<script src="./index.bundle.js"></script>
<script src="//cdn.rawgit.com/donmccurdy/aframe-extras/v3.13.1/dist/aframe-extras.min.js"></script>
<style> ... </style>
</head>
<body>
<div id="app"></div>
<script src="./app.bundle.js" defer></script>
</body>
webpack.config.js
'use strict'
const webpack = require('webpack')
const path = require('path')
const extractCommons = new webpack.optimize.CommonsChunkPlugin({
name: 'commons',
filename: 'commons.js'
})
const config = {
context: path.resolve(__dirname, 'client'),
entry: {
index: './index.js',
app: './app.jsx'
},
output: {
path: path.resolve(__dirname, 'public'),
filename: '[name].bundle.js'
},
devtool: 'source-map',
resolve: {
extensions: ['.js', '.jsx']
},
module: {
loaders: [
{
test: /jsx?$/,
include: path.resolve(__dirname, 'client'),
loader: 'babel-loader',
query: {
presets: ['react', 'es2015']
}
}
]
},
plugins: [
extractCommons
]
};
module.exports = config
index.js
The server-side code for the app. I have seen others catch their mistakes in this general location so I'll put this up for good measure.
'use strict'
const express = require('express');
const path = require('path');
const app = express();
const production = process.env.NODE_ENV === 'production';
const port = production ? process.env.PORT : 8080;
var publicPath = path.resolve(__dirname, '../public');
app.use(express.static(publicPath));
app.use('/', (req, res, send) => {
res.sendFile(path.resolve(__dirname, '..', 'index.html'))
})
app.listen(port, () => {
console.log(`Virtual Reality Enabled On Port ${port}`);
});
If your npm script build-client builds your webpack bundle. Reading your webpack.config.js tells me that the bundles will be in a directory called public and all your file names will have .bundle.js as a suffix. So you should change your Procfile to web: npm run build-client && node public/index.bundle.js
Related
So I've a bug I've been chasing all day, and I've narrowed it down to my webpack config (I think).
I run my app locally using webpack server and index.html is displayed correctly (with the relevant script file).
I build my project locally, right-click index.html and open with live-server, and index.html is displayed correctly
I upload my files to my server, get Express to return index.html, and it returns index.html (I can tell because the script tag refers to index.js and the title tab says home), but the content is that of admin.html
Deleting the contents of index.html on the server to a simple page confirms (imo) that it isn't an issue of Express serving the wrong file
So it must be a webpack issue, right?
What I don't understand is why webpack server and live server display the content correctly, but the (apparently) same file doesn't in production. I probably don't have the correct webpack.prod.js config settings (they're basically empty), but I'm not experienced enough to spot the issue, so would appreciate if someone has the time to look
My file structure locally:
|-dist
|
|-src
| |-- assets
| |-- css
| |-- js
| |-- admin.html
| |-- index.html
|
|-webpack.common.js
|-webpack.dev.js
|-webpack.prod.js
|-package.json
|-// more config files
// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/js/controllers/indexController.js',
admin: './src/js/controllers/adminController.js'
},
output: {
filename: '[name].bundle.[chunkhash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
module: {
rules: [
{
test:/\.css$/i,
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.js$/i,
use: ['babel-loader'],
exclude: /node_modules/,
},
{
test: /\.(png|svg|jpg|jpeg|gif|webp)$/i,
type: 'asset/resource'
},
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
filename: 'admin.html',
template: './src/admin.html',
chunks: ['admin']
})
]
}
// webpack.dev.js
const path = require('path');
const {merge} = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: "development",
devServer: {
static: {
directory: path.resolve(__dirname, 'dist'),
},
port: 4600,
open: {
app: {
name: 'chrome',
},
},
hot: true,
compress: true,
historyApiFallback: true,
watchFiles: ["src/**/*"]
},
});
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
*Edit I still think the server is sending the correct html file because the title and js src reference index, however the source now does look wrong (it has been correct in the past):
// var/www/my-site/express-app/public/html/index.html
<script defer="defer" src="./src/js/controllers/index.bundle.0bb3ac28e5969d907928.js"></script>
But I've no idea why the file was previously displaying another page's html (despite me removing that page, admin.html, entirely). It's no longer doing that, just complaining about the path to the file - which is progress.
Server file structure
// var/www/my-site/express-app
|-node_modules
|
|-public
| |-- assets
| |-- css
| |-- js
| |-- admin.html
| |-- index.html
|
|-package-lock.json
|-package.json
|-app.js
// app.js
const express = require('express');
const path = require('path');
const app = express();
const port = 3000;
app.use(express.static('public'));
app.get('/index', (req, res, next) => {
console.log('path:', path.join(__dirname, 'public', 'index.html'));
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.get('/', (req, res, next) => {
console.log('path:', path.join(__dirname, 'public', 'index.html'));
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.use((error, req, res, next) => {
res.status(500).json({msg: 'Please contact your web administrator'});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
I'm trying to set up a webpack server with Express.
When I use a simple script, such as the following:
document.getElementById('testing').innerHTML = "It's a test";
if (module.hot) {
module.hot.accept();
}
The server works fine, both from the same machine and from other devices in the same network.
However, when I try to require Express, it breaks with 25 errors.
Here's what I've got:
webpack.config.js
const path = require('path');
module.exports = {
entry: './app/serverTest.js',
mode: 'development',
output: {
filename: 'bundled.js',
path: path.resolve(__dirname, 'app')
},
devServer: {
before: (app, server) => {
server._watch('./app/**/*.html');
},
contentBase: path.join(__dirname, 'app'),
hot: true,
port: 4000,
host: '0.0.0.0'
},
}
serverTest.js
let express = require('express');
let app = express();
alert('This is a test');
I'm not even using the Express code. Just by requiring it, I get errors.
Here's the VSCode console logging: https://justpaste.it/342o0 (external link because it's very long)
I am trying to deploy a website with a basic express server(inexperienced with it obviously) when I use a node dev server it works fine but when I use node server.js i get this error
Uncaught SyntaxError: Cannot use import statement outside a module
So I have a main.js with imports as follows:
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import { defaults as defaultControls } from 'ol/control';
I have my basic server.js
const express = require('express');
const path = require('path');
const port = process.env.PORT || 3003;
const app = express();
app.use(express.static(__dirname));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, './frontend/index.html'));
});
app.listen(port);
Start script
"start": "node server.js",
and webpack config
module.exports = {
entry: './main.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: './main.js'
},
devtool: 'source-map',
devServer: {
port: 3003,
clientLogLevel: 'none',
stats: 'errors-only'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new CopyPlugin([{from: 'data', to: 'data'}]),
new HtmlPlugin({
template: './frontend/index.html'
})
]
};
folder structure
frontend -index.html
-page2.html
main.js
package.json
webpack
...
Here is my folder structure:
src/
└───public/
├───index.js
├───controllers/
├───css/
├───js/
├───models/
├───vendor/
└───views/
└───partials/
I know it's not the best folder structure, but it's one of my earlier projects, and I wanted to test webpack on it.
Anyways the controllers, js, vendor all contain javascript code.
My webpack.dev.config is on the same level as src:
const path = require("path");
const common = require("./webpack.common");
const {merge} = require("webpack-merge");
module.exports = merge(common, {
target: 'node',
mode: "development",
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
],
module: {
}
});
As you can see it merges from webpack.common.js:
const nodeExternals = require('webpack-node-externals');
module.exports = {
externals: [nodeExternals()],
entry: {
main: './src/index.js'
},
module: {
rules: [
{
test: /\.js$/,
}
]
}
}
For now I am hardcoding the location of the bundled js file in my HTML docs, but on the browser when I run this, I see the message Uncaught ReferenceError: require is not defined and I am not sure how to fix it.
Here is index.js:
const express = require('express');
const path = require('path');
const app = express();
const port = process.env.PORT || 8080;
const exphbs = require('express-handlebars');
const Datastore = require('nedb');
const db = new Datastore({
filename: path.join(__dirname, 'public/db/pfam.db'),
autoload: true
});
module.exports.db = db;
app.use(express.static(__dirname + "/public"));
app.set('views', path.join(__dirname, 'public/views'));
app.set('img', path.join(__dirname, 'public/img'));
app.set('js', path.join(__dirname, 'public/js'));
app.set('css', path.join(__dirname, 'public/css'));
app.set('controllers', path.join(__dirname, '/public/controllers'));
app.set('db', path.join(__dirname, '/public/db'));
app.engine('handlebars', exphbs({
defaultLayout: 'home',
layoutsDir: path.join(__dirname, '/public/views')
}))
app.set('view engine', 'handlebars');
app.use(require('./public/controllers'))
console.log(`listening on port ... ${port}`)
app.listen(port);
The reason you are getting Uncaught ReferenceError: require is not defined is because your bundles contains require keyword, which is undefined in the browser?
Why do they contain require?
Because you use nodeExternals from webpack-node-externals
From their docs:
All node modules will no longer be bundled but will be left as require('module').
I tried using webpack-dev-middleware as a middleware.
I bundles in memory as it should and serves the JS output file,
but it doesn't hot reload when I save.
any ideas?
You'll want to use https://www.npmjs.com/package/webpack-hot-middleware or something similar.
You should use webpack-hot-middleware. here is a working example. I hope it helps.
for your webpack config (lets call it webpack.config.dev):
const path = require('path');
const webpack = require('webpack');
const distPath = path.resolve(__dirname, '../dist');
const srcPath = path.resolve(__dirname, '../src');
module.exports = {
context: srcPath,
target: 'web',
entry: [
'react-hot-loader/patch',
// activate HMR for React
// bundling the client for webpack-dev-server
// and connect to the provided endpoint
'webpack-hot-middleware/client',
'./client/index.js'
// the entry point of your app
],
output: {
filename: 'app.js',
// the output bundle
path: distPath,
publicPath:'/static/',
// necessary for HMR to know where to load the hot update chunks
pathinfo: true
},
module: {
rules: [
// eslint checking before processed by babel
{test: /\.js$/, enforce: 'pre', loader: 'eslint-loader', exclude: /node_modules/},
// babel
{test: /\.js$/, use: [{loader: 'babel-loader'}], exclude: /node_modules/}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// enable HMR globally
new webpack.DefinePlugin({ "process.env": { NODE_ENV: '"development"' } })
]
};
For the server (called index.dev.js):
import path from 'path';
import express from 'express';
import React from 'react';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import { renderToString } from 'react-dom/server';
// the above file
import webpackConfig from '../../webpack/webpack.config.dev';
// myown react Html component
import Html from '../server/Html';
const app = express();
app.use(express.static(path.join(__dirname, '..', './public')));
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
quiet: true,
noInfo: true,
publicPath: webpackConfig.output.publicPath,
stats: { colors: true }
}));
app.use(webpackHotMiddleware(compiler));
app.get('*', (req, res) =>
res.status(200).send(`<!doctype html>${renderToString(
<Html />)}`
// This is my own Html react component. You can send a static html file here as well.
)
);
app.listen(3000, (err) => {
if (err) {
console.error(err);
return;
}
console.info('Demo app listening on port 3000');
});
At the end I call it using babel-watch:
"scripts": {
"start:dev": "rm -f ./dist/* && ./node_modules/.bin/babel-watch ./src/server/index.dev.js -w ./src/server",
},