I have recently switched from using 'npm start' script that comes with create-react-app to Weback's 'npm run build' and then running the server. After slowly getting all the dependencies working I got my bundle to complete building. However, when I use 'npm start' the server starts and nothing shows up on my website.
I tried inputting <div id="root"> into my .html file in my "public" directory, but it didn't work. I then found out Webpack created a second for me? Either way placing a <div> or even a <script> in either one doesn't work.
I have updated all of my dependencies for react so that the ReactDOM could render peacefully. my React code worked perfectly before I switched to webpack so I'm sure it's not a problem with my index.js and whatever it imports.
I'm convinced it's a problem in webpack, but I've tried plenty of different presets in webpack.config.js and can't seem to find any that works (I have forgotten the ones I've tried so I will take any suggestions).
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: './main.js',
path: path.resolve(__dirname, '/dist')
},
plugins: [new webpack.ProgressPlugin(), new HtmlWebpackPlugin(), new webpack.IgnorePlugin(/^pg-native$/)],
module: {
rules: [
{
test: /.(js|jsx)$/,
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
options: {
plugins: [
'syntax-dynamic-import'
],
presets: [
'#babel/preset-env',
'#babel/preset-react',
{
'plugins': ['#babel/plugin-proposal-class-properties']
}
]
}
},
{
test:/\.(s*)css$/,
use:['style-loader','css-loader', 'sass-loader']
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
loader: 'url-loader?limit=100000'
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/
}
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: true
}
},
node: {
fs: 'empty'
}
,
devServer: {
open: true
}
};
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './App.js';
import * as serviceWorker from './serviceWorker';
let state = {};
window.setState = (changes) => {
state = Object.assign({}, state, changes);
ReactDOM.render(
<App {...state}/>,
document.getElementById('root')
);
}
/* eslint no-restricted-globals: 0*/
let initialState = {
name: 'Name Test',
location: location.pathname.replace(/^\/?|\/$/g, "")
};
window.setState(initialState);
serviceWorker.unregister();
<!-- .HTML created by Webpack -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
<!-- .HTML created by create-react-app (before webpack)-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Skedge</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
when I go into the web console this shows up as an error -
Uncaught Invariant Violation: Target container is not a DOM element.
at invariant (webpack:///./node_modules/react-dom/cjs/reactdom.development.js?:55:15)
at Object.render (webpack:///./node_modules/react-dom/cjs/reactdom.development.js?:21151:36)
at eval (webpack:///./src/index.js?:35:50)
at Module../src/index.js (http://localhost:8080/main.js:3970:1)
at webpack_require (http://localhost:8080/main.js:20:30)
at eval (webpack:///multi_(webpack)-dev-server/client?:2:18)
at Object.0 (http://localhost:8080/main.js:4015:1)
at webpack_require (http://localhost:8080/main.js:20:30)
at http://localhost:8080/main.js:84:18
at http://localhost:8080/main.js:87:10
It says it doesn't understand the ReactDOM.render() function.
I even threw in a <h1>Test</h1> to see if it was my code but it still bugged out, so I'm sure it's something in my webpack.config.js that isn't right but I just can't figure out what.
Edit
I gave up on using webpack, I undid my npm eject and went back to just using npm start.
Related
I've been trying to setup react-helmet with server-side-rendering. I followed the docs and some blog posts on how to setup react-helmet with SSR, but have been unable to produce the desired results. Here's a code snippet of how I'm rendering the App:
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './src/App';
const express = require('express');
const app = express();
app.get('*', (req, res) => {
const app = renderToString(<App />);
const helmet = Helmet.renderStatic();
res.send(formatHTML(app, helmet));
})
function formatHTML(appStr, helmet) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
${helmet.title.toString()}
${helmet.meta.toString()}
</head>
<body>
<div id="root">
${ appStr }
</div>
<script src="./bundle.js"></script>
</body>
</html>
`
}
When I run the above code, I get an error saying 'cannot use import statement outside a module'. Is it possible to use both es5 and es6 syntax at the same time? Or is there is better way to setup React-helmet?
This is my babel configuration file
{
"presets": [
[
"#babel/preset-env",
{
"modules": false
}
],
"#babel/preset-react",
"#babel/preset-flow"
],
"env": {
"development": {
"only": [
"app",
"internals/scripts"
],
"plugins": [
"#babel/plugin-transform-react-jsx-source"
]
},
"production": {
"only": [
"app"
],
"plugins": [
"transform-react-remove-prop-types",
"#babel/plugin-transform-react-constant-elements",
"#babel/plugin-transform-react-inline-elements"
]
},
"test": {
"plugins": [
"#babel/plugin-transform-modules-commonjs",
"dynamic-import-node"
]
}
},
"compact": true,
"plugins": [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-syntax-import-meta",
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-json-strings",
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"#babel/plugin-proposal-function-sent",
"#babel/plugin-proposal-export-namespace-from",
"#babel/plugin-proposal-numeric-separator",
"#babel/plugin-proposal-throw-expressions",
"#babel/plugin-proposal-export-default-from",
"#babel/plugin-proposal-logical-assignment-operators",
"#babel/plugin-proposal-optional-chaining",
[
"#babel/plugin-proposal-pipeline-operator",
{
"proposal": "minimal"
}
],
"#babel/plugin-proposal-nullish-coalescing-operator",
"#babel/plugin-proposal-do-expressions",
"#babel/plugin-proposal-function-bind",
"lodash"
]
}
You need to wrap your server using #babel/register.
This is how I handle that for my TypeScript CRA projects without ejecting.
NOTICE: I use this method to inject metadata into index.html vs render the whole app (some components I use don’t play well with SSR).
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div id="root"></div>
</body>
</html>
index.js
"use strict"
require("ignore-styles")
require("#babel/register")({
ignore: [/(node_modules)/],
presets: [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-typescript",
],
extensions: [".tsx"],
cache: false,
})
require("./server")
server.js (excerpt)
const indexPath = path.join(__dirname, "build/index.html")
const middleware = async (req, res, next) => {
let context = {}
let html = renderToString(
React.createElement(StaticRouter, {
location: req.url,
context: context,
})
)
const helmet = Helmet.renderStatic()
if (context.url) {
res.redirect(context.url)
} else if (!fs.existsSync(indexPath)) {
next("Site is updating... please reload page in a few minutes.")
} else {
let index = fs.readFileSync(indexPath, "utf8")
let status = 200
if (typeof context.status === "number") {
status = context.status
}
return res.status(status).send(
index
.replace('<div id="root"></div>', `<div id="root">${html}</div>`)
.replace("</head>", `${helmet.meta.toString()}</head>`)
.replace("</head>", `${helmet.title.toString()}</head>`)
.replace("</head>", `${helmet.script.toString()}</head>`)
)
}
}
server.get("/", middleware)
server.use(express.static(path.join(__dirname, "build")))
server.get("*", middleware)
I'm trying to do server-side rendering my react app but it doesn't work. It shows document not define when trying to run. This error only shows when I used CSS file in app.js file. Though client-side render working properly.
//My App.js
import React from 'react';
import { renderRoutes } from 'react-router-config';
import ErrorBoundary from './validation/ErrorBoundry';
import './assets/css/bootstrap.min.css';
import './assets/scss/main.css';
const App = ({ route }) => {
return (
<div className="App">
<ErrorBoundary>{renderRoutes(route.routes)}</ErrorBoundary>
</div>
);
};
export default App;
//webpack.base.js
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
],
}
]
},
}
error screenshot
The document object is the root node of the HTML document.
This object does not exist in node application.
And the way to handle it in node application can be done by using jsdom.
jsdom is a pure-JavaScript implementation of many web standards,
notably the WHATWG DOM and HTML Standards, for use with Node.js
That mistake happens because, Webpack transforms the CSS to the following code:
var css_main_css = document.createElement("style");
css_main_css.innerHTML = "*css*";
document.querySelector("head").appendChild(css_main_css);
I have the following code that is currently WORKING code
Form:
<form id="demoForm" method="POST">
// bootstrap form...
</form>
on page Script:
<script src="~/scripts/Framework/jquery-3.3.1.min.js"></script>
<script src="~/scripts/Framework/bootstrap-4/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.3/es6-shim.min.js"></script>
<script src="~/scripts/Plugins/formvalidation-1.3.0/js/FormValidation.full.min.js"></script>
<script src="~/script/Plugins/formvalidation-1.3.0/js/plugins/Bootstrap.min.js"></script>
<script src="~/script/Plugins/formvalidation-1.3.0/js/plugins/J.min.js"></script>
<script>
$(function(){
$('#demoFrom').formValidation({
plugins: {
declarative: new FormValidation.plugins.Declarative({ html5Input: true }),
trigger: new FormValidation.plugins.Trigger(),
bootstrap: new FormValidation.plugins.Bootstrap(),
submitButton: new FormValidation.plugins.SubmitButton(),
icon: new FormValidation.plugins.Icon({
valid: 'fa fa-check',
invalid: 'fa fa-times',
validating: 'fa fa-refresh'
})
}
});
});
</script>
But when I try with requirejs its not. The NOT WORKING code below:
<script src="~/scripts/Framework/require.js" data-main="~/scripts/app.js">
App.js:
require.config({
baseUrl: '/Scripts/',
paths: {
jquery: 'Framework/jquery-3.3.1.min',
bootstrap:'Framework/bootstrap-4/js/bootstrap.bundle.min',
es6Shim: 'Framework/es6-shim.min',
fvMain: 'Plugins/formvalidation-1.3.0/js/FormValidation.full.min',
fvBootstrap: 'Plugins/formvalidation-1.3.0/js/plugins/Bootstrap.min',
fvJQuery: 'Plugins/formvalidation-1.3.0/js/plugins/J.min'
},
shim: {
jquery: {
exports: '$'
},
bootstrap: {
deps:['jquery']
},
fvBootstrap: {
deps: ['es6Shim', 'fvMain','fvJQuery']
}
}
});
require(['jquery', 'bootstrap'],
function ($) {
//... some other tasks
require[('fvBootstrap'), function(){
$('#demoFrom').formValidation({
plugins: {
declarative: new FormValidation.plugins.Declarative({ html5Input: true }),
trigger: new FormValidation.plugins.Trigger(),
bootstrap: new FormValidation.plugins.Bootstrap(),
submitButton: new FormValidation.plugins.SubmitButton(),
icon: new FormValidation.plugins.Icon({
valid: 'fa fa-check',
invalid: 'fa fa-times',
validating: 'fa fa-refresh'
})
}
});
});
});
in the requirejs code I am getting the error "FormValidation is not defined"
I have tried to contact the developer with this issue without any luck. Can anyone help me on this, please?
Check your imports again, i guess you are including this library but actually you don't have it in your directory.
check this import
<script src="~/scripts/Plugins/formvalidation-1.3.0/js/FormValidation.full.min.js"></script>
I attempted to modify vue-hackernews-2.0 to support lazy-loaded routes using Webpack's code-splitting feature as per instructions found at these links:
https://router.vuejs.org/en/advanced/lazy-loading.html
http://vuejs.org/guide/components.html#Async-Components
However, I ran into some issues. When I loaded the app in the browser, all suggested variations of syntax triggered Module not found errors on the server-side when attempting to load in the server-side chunks.
Given this wrapper around the code-split points in router.js...
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
// INSERT CODE-SPLIT POINT SYNTAXES HERE (they are below)
export default new Router({
mode: 'history',
routes: [{
path: '/',
component: Home
}, {
path: '/foo',
component: Foo
}]
})
All of these variations of syntax threw the Module not found error:
Variation 1:
const Home = () => System.import('./views/Home.vue')
const Foo = () => System.import('./views/Foo.vue')
Variation 2:
const Home = (resolve) => require(['./views/Home.vue'], resolve)
const Foo = (resolve) => require(['./views/Foo.vue'], resolve)
Variation 3:
const Home = (resolve) => {
require.ensure(['./views/Home.vue'], () => {
resolve(require('./views/Home.vue'))
})
}
const Foo = (resolve) => {
require.ensure(['./views/Foo.vue'], () => {
resolve(require('./views/Foo.vue'))
})
}
The error message was always along the lines of:
(note: this error is adapted from a small reproduction I made of the issue, not from the hackernews example)
Error: Cannot find module './0.server.js'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
at require (internal/module.js:20:19)
at Function.requireEnsure [as e] (__vue_ssr_bundle__:42:25)
at Home (__vue_ssr_bundle__:152:30)
at /Users/razorbeard/projects/vue-2-ssr/node_modules/vue-router/dist/vue-router.js:1421:19
at iterator (/Users/razorbeard/projects/vue-2-ssr/node_modules/vue-router/dist/vue-router.js:1277:5)
at step (/Users/razorbeard/projects/vue-2-ssr/node_modules/vue-router/dist/vue-router.js:1213:9)
at step (/Users/razorbeard/projects/vue-2-ssr/node_modules/vue-router/dist/vue-router.js:1217:9)
I tried adapting my code to use the suggestions offered at Server-side react with webpack 2 System.import, but those did not work either.
I read a post that described configuring a build-time global variable using Webpack's DefinePlugin that allowed me to inspect whether the code was running on the server or on the client - this allows me to code-split on the client, but just bundle everything in on the server.
In the server webpack config:
{
...
plugins: [
new webpack.DefinePlugin({
BROWSER_BUILD: false
})
]
...
}
In the client webpack config:
{
...
plugins: [
new webpack.DefinePlugin({
BROWSER_BUILD: true
})
]
...
}
Then, in the same wrapper-snippet as above for the router.js file, I used this variation of syntax:
const Home = BROWSER_BUILD ? () => System.import('./views/Home.vue') : require('./views/Home.vue')
const Foo = BROWSER_BUILD ? () => System.import('./views/Foo.vue') : require('./views/Foo.vue')
This made rendering work - partially. Navigating directly to the app in the browser (and respective routes) server-rendered the correct UI. Clicking around, vue-router's client-side logic took me to the right UI. Everything seemed hunky-dory - until I opened DevTools:
The same issue also occurs if a module is loaded lazily as a subcomponent of a route:
<template>
<div class="page">
<heading></heading>
</div>
</template>
<script>
const Heading = BROWSER_BUILD ? () => System.import('./Heading.vue') : require('./Heading.vue')
export default {
components: {
Heading
}
}
</script>
I tried asking for some help in the official Vue forum, but came up empty: http://forum.vuejs.org/t/2-0-help-needed-with-server-rendered-lazy-routes/906
Thinking this might be a bug with vue-router, I opened an issue there: https://github.com/vuejs/vue-router/issues/820
Unfortunately, I wasn't able to find a solution.
So, I put together a small repo that reproduces the issue: https://github.com/declandewet/vue2-ssr-lazy-error
I have a hunch that the actual problem might be coming from https://www.npmjs.com/package/vue-server-renderer.
I'm really stuck on this and am used to how easy it is to do in react - and would really appreciate any help/tips/direction towards a solution!
Here is the webpack config from the reproduction repo for convenience:
import fs from 'fs'
import path from 'path'
import webpack from 'webpack'
import validate from 'webpack2-validator'
import { dependencies } from './package.json'
let babelConfig = JSON.parse(fs.readFileSync('./.babelrc'))
/* turn off modules in es2015 preset to enable tree-shaking
(this is on in babelrc because setting it otherwise causes issues with
this config file) */
babelConfig.presets = babelConfig.presets.map(
(preset) => preset === 'es2015' ? ['es2015', { modules: false }] : preset
)
const babelOpts = {
...babelConfig,
babelrc: false,
cacheDirectory: 'babel_cache'
}
const SHARED_CONFIG = {
devtool: 'source-map',
module: {
loaders: [{
test: /\.vue$/,
loader: 'vue'
}, {
test: /\.js$/,
loader: 'babel',
exclude: 'node_modules',
query: babelOpts
}]
},
resolve: {
modules: [
path.join(__dirname, './src'),
'node_modules'
]
}
}
const SERVER_CONFIG = validate({
...SHARED_CONFIG,
target: 'node',
entry: {
server: './src/server.js',
renderer: './src/renderer.js'
},
output: {
path: path.join(__dirname, './dist'),
filename: '[name].js',
chunkFilename: '[id].server.js',
libraryTarget: 'commonjs2'
},
plugins: [
new webpack.DefinePlugin({
BROWSER_BUILD: false,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),
new webpack.BannerPlugin({
banner: 'require("source-map-support").install();',
raw: true,
entryOnly: false
})
],
externals: Object.keys(dependencies)
})
const CLIENT_CONFIG = validate({
...SHARED_CONFIG,
entry: {
app: './src/client.js',
vendor: ['vue']
},
output: {
path: path.join(__dirname, './dist/assets'),
publicPath: '/',
filename: 'bundle.js'
},
plugins: [
new webpack.DefinePlugin({
BROWSER_BUILD: true,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor.js'
})
]
})
export default [SERVER_CONFIG, CLIENT_CONFIG]
EDIT: Noticing that in React, we use match on the client to get the right route config for the current view, I decided to inspect what components were getting matched using app.$router.getMatchedComponents() and found something interesting:
Server Entry:
import app from './app'
export default (context) => {
// using app.$router instead of importing router itself works
// (not sure why the hacker-news example imports the router module instead...)
app.$router.push(context.url)
const components = app.$router.getMatchedComponents()
console.log('server-side', components)
return Promise.all(components.map((component) => component))
.then(() => app)
}
When navigating to the home page, this logs to the terminal:
server-side [ { __file: '/Users/razorbeard/projects/vue-2-ssr/src/views/Home.vue',
render: [Function],
staticRenderFns: [ [Function] ] } ]
Client Entry:
import app from './app'
const components = app.$router.getMatchedComponents()
console.log('client-side', components)
// kickoff client-side hydration
Promise.all(components.map((component) => Promise.resolve(component)))
.then(() => app.$mount('#app'))
When navigating to the home page, this logs to the devtools console:
client-side []
As you can see, no components are getting matched on the client side.
I'm trying to use webpack's Hot Module Replacement plugin. I've managed to randomly get it working, but it still isn't doing quite what I would hope it to.
Basically, I get no messages in my console that it's even active, though it's building without issue and file watching is working, as I get the messages webpack: bundle is now VALID and webpack: bundle is now INVALID when I update.
webpack, webpack-dev-server, and react-hot are all installed locally.
But in the browser's console, the only thing I see is:
Download the React DevTools for a better development experience: https://fb.me/react-devtools
I'm using Laravel to update my index file based on an environment variable and it is working just fine.
Here is the index.php file:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="content"></div>
#if(env("APP_HOTRELOAD"))
<script src="http://localhost:8080/js/vendor.js"></script>
<script src="http://localhost:8080/js/app.js"></script>
#else
<script src="js/vendor.js"></script>
<script src="js/app.js"></script>
#endif
</body>
</html>
Here is the webpack config file (webpack.hot-reload.config.js):
var path = require("path");
var webpack = require("webpack");
var node_modules = path.resolve(__dirname, "node_modules");
var public_dir = path.resolve(__dirname, "public");
module.exports = {
debug: (process.env.NODE_ENV === "production"),
entry: {
vendor: [
"es5-shim",
"es5-shim/es5-sham",
"babel-core/polyfill",
"babel-core/external-helpers",
"react",
"react-router-component"
],
app: [
"webpack-dev-server/client?http://localhost:8080",
"webpack/hot/only-dev-server",
path.resolve(__dirname, "resources/assets/js/index.js")
]
},
contentBase: public_dir,
output: {
path: path.resolve(public_dir, "js"),
filename: "app.js",
publicPath: "/"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin("vendor", "vendor.js"),
//This is necessary for React to know whether it's supposed to strip out
//addons and extra stuff when being minified. Essentially, it becomes dead
//code and webpack will take it out.
new webpack.DefinePlugin({
"process.env": {"NODE_ENV": JSON.stringify(process.env.NODE_ENV)}
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [
{
test: /\.(sa|c)ss$/,
loader: "css!style!sass"
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loaders: [
"react-hot",
"strip-loader?strip[]=debug,strip[]=console.log,strip[]=console.error",
"babel-loader"
]
}
]
},
resolve: {
root: path.resolve(__dirname, "resources/assets/js"),
extensions: ["", ".js", ".json"]
}
};
In order to start the webpack-dev-server, I use a separate server.js file, executed by using node server.js:
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.hot-reload.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
contentBase: config.contentBase,
hot: true,
historyApiFallback: true,
quiet: false,
noInfo: false,
stats: {
colors: true
}
}).listen(8080, 'localhost', function (err, result) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:8080');
});
It seems to work randomly after waiting some time, but if I change a file or refresh the page manually, it seems to just break. I've tried using both Firefox and Chrome and it doesn't make a difference, so I'm thinking it's in the build.
What could be wrong?
I figured it out. There was a comment about it on the page that notes how to use webpack-dev-server, but I managed to read over it.
If you look in my config you'll see:
...
output: {
path: path.resolve(public_dir, "js"),
filename: "app.js",
**publicPath: "/"**
},
...
I misinterpreted the publicPath key and its path.
However, the example given in the docs shows:
module.exports = {
entry: {
app: ["./app/main.js"]
},
output: {
path: "./build",
publicPath: "/assets/",
filename: "bundle.js"
}
};
And states:
This modified bundle is served from memory at the relative path specified in publicPath (see API). It will not be written to your configured output directory. Where a bundle already exists at the same url path the bundle in memory will take precedence.
However, for this example, this bundle will be served from /, not /assets/ because further down, the content base is given as build/. There's nothing that notes that the directory where the scripts lie is possibly aliased to /assets/ at all, so that's why I placed the / path as the publicPath instead of the subdirectory my JS was actually being served from..
The docs note that:
To teach webpack to make requests (for chunk loading or HMR) to the webpack-dev-server you need to provide a full URL in the output.publicPath option.
So I changed:
publicPath: "/"
to:
publicPath: "http://localhost:8080/js/"
And now my files are being served up correctly. I added the /js/ because that's where I my JavaScript is served from on the actual server.