webpack: "Uncaught SyntaxError: Unexpected token <" error happened sometimes

Currently I am working with webpack2 (beta-20) + React + express to create an universal web app. But when I build my script in development environment, sometimes I got an error

Uncaught SyntaxError: Unexpected token <

When I look into the file, it show the error occurs on

<!DOCTYPE html>

And this error will happened in both development and production. And when I remove all of the [hash] and [chunkhash], this error won’t happened. I don’t know why, does anyone has solution for this problem?

Here’s my webpack.config.js :

const path = require('path');
const webpack = require('webpack');
const AssetsPlugin = require('assets-webpack-plugin');
const del = require('del');

const nodeEnv = process.env.NODE_ENV || 'development';
const isDev = nodeEnv !== 'production';

const rootPath = path.join(__dirname);
const srcPath = path.join(rootPath, './src');
const staticPath = path.join(rootPath, './static');
const cleanPath = [
  path.join(staticPath, './*'),
  path.join('!', staticPath, './favicon.ico'),
  path.join('!', staticPath, './assets.js'),
];

// The plug of cleaning static files, it used for production settings
class CleanPlugin {
  constructor(options) {
    this.options = options;
  }

  apply() {
    del.sync(this.options.files);
  }
}

// Setting the plugins for development and prodcution
function getPlugins() {
  const plugins = [];

  plugins.push(
    new webpack.DefinePlugin({
      'process.env': { NODE_ENV: JSON.stringify(nodeEnv) },
      __DEV__: JSON.stringify(isDev),
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: isDev ? '[name].[hash].js' : '[name].[chunkhash].js',
      minChunks: Infinity,
    }),
    new webpack.NoErrorsPlugin(),
    new webpack.optimize.OccurrenceOrderPlugin(),
    new AssetsPlugin({
      filename: 'assets.js',
      path: staticPath,
      processOutput: assets => `module.exports = ${JSON.stringify(assets)};`,
    })
  );

  if (isDev) {
    plugins.push(
      new webpack.HotModuleReplacementPlugin()
    );
  } else {
    plugins.push(
      new CleanPlugin({ files: cleanPath }),
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false,
      }),
      new webpack.optimize.UglifyJsPlugin({
        compress: { screw_ie8: true, warnings: false },
        output: { comments: false },
        sourceMap: false,
      }),
      new webpack.optimize.DedupePlugin()
    );
  }

  return plugins;
}

// Webpack settings
module.exports = {
  cache: isDev,
  debug: isDev,
  devtool: isDev ? 'cheap-module-eval-source-map' : 'hidden-source-map',
  context: srcPath,
  entry: {
    app: isDev ? [
      'react-hot-loader/patch',
      'webpack-hot-middleware/client',
      './client.js',
    ] : './client.js',
    vendor: [
      'react', 'react-dom',
      'redux', 'react-redux',
      'redux-thunk',
      'react-router',
      'react-router-redux',
      'react-helmet',
      'axios',
    ],
  },
  output: {
    path: staticPath,
    publicPath: '/',
    filename: isDev ? '[name].[hash].js' : '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].js',
  },
  module: {
    preLoaders: [
      { test: /\.jsx?$/, loader: 'eslint', exclude: /node_modules/ },
    ],
    loaders: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel',
        query: {
          cacheDirectory: isDev,
          babelrc: false,
          presets: [['es2015', { modules: false }], 'react', 'stage-0'],
          plugins: ['transform-runtime', 'react-hot-loader/babel'],
        },
      },
    ],
  },
  resolve: {
    extensions: ['', '.js', '.jsx', '.css', '.scss'],
    modules: [
      srcPath,
      'node_modules',
    ],
  },
  plugins: getPlugins(),
  eslint: { failOnError: true },
};

Here’s my server.js :

import path from 'path';
import express from 'express';
import favicon from 'serve-favicon';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import { match, RouterContext } from 'react-router';
import routes from './routes';
import config from './config';
import configureStore from './configureStore';
import renderHtmlPage from './renderHtmlPage';

const app = express();

app.use(favicon(path.join(__dirname, '../static/favicon.ico')));
app.use('/static', express.static(path.join(__dirname, '../static')));

// Run express as webpack dev server
if (__DEV__) {
  const webpack = require('webpack');
  const webpackConfig = require('../webpack.config');

  const compiler = webpack(webpackConfig);

  app.use(require('webpack-dev-middleware')(compiler, {
    publicPath: webpackConfig.output.publicPath,
    noInfo: true,
    hot: true,
    stats: { colors: true },
  }));

  app.use(require('webpack-hot-middleware')(compiler));
}

// Render content
app.get('*', (req, res) => {
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.status(500).send(error.message);
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search);
    } else if (!renderProps) {
      res.sendStatus(404);
    } else {
      const store = configureStore();

      const promises = renderProps.components
        .filter(component => component.fetchData)
        .map(component => component.fetchData(store.dispatch, renderProps.params));

      Promise.all(promises)
        .then(() => {
          const content = renderToString(
            <Provider store={store}>
              <RouterContext {...renderProps} />
            </Provider>
          );
          const initialState = store.getState();

          res.status(200).send(renderHtmlPage(content, initialState));
        });
    }
  });
});

if (config.port) {
  app.listen(config.port, config.host, err => {
    if (err) console.error(`==> 😭  OMG!!! ${err}`);

    console.info(`==> 🌎  Listening at http://${config.host}:${config.port}`);
  });
} else {
  console.error('==> 😭  OMG!!! No PORT environment variable has been specified');
}

Here’s my client.js :

import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import { syncHistoryWithStore, routerReducer } from 'react-router-redux';
import configureStore from './configureStore';
import routes from './routes';

const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState, routerReducer);
const history = syncHistoryWithStore(browserHistory, store);

const renderApp = (CurrentAppRoutes: any) => {
  render(
    <AppContainer>
      <Provider store={store}>
        <Router history={history} routes={CurrentAppRoutes} />
      </Provider>
    </AppContainer>,
    document.getElementById('react-view')
  );
};

renderApp(routes);

// Enable hot reload by react-hot-loader
if (__DEV__) {
  if (module.hot) {
    module.hot.accept('./routes', () => {
      const NextAppRoutes = require('./routes').default;

      renderApp(NextAppRoutes);
    });
  }
}

Here’s my renderHtmlPage.js :

import Helmet from 'react-helmet';
import assets from '../static/assets';

export default (content, initialState) => {
  const head = Helmet.rewind();

  // Setup html page
  return `
    <!DOCTYPE html>
    <html ${head.htmlAttributes.toString()}>
      <head>
        <meta char-set="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <meta http-equiv="Content-Language" content="en" />
        <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />

        ${head.base.toString()}
        ${head.title.toString()}
        ${head.meta.toString()}
        ${head.link.toString()}
      </head>
      <body>
        <div id="react-view">${content || null}</div>

        <script type="text/javascript">
          ${initialState && `window.__INITIAL_STATE__=${JSON.stringify(initialState)}`}
        </script>

        <!--[if gte IE 9 ]>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-sham.min.js"></script>
        <![endif]-->

        ${assets && `<script src="${assets.vendor.js}"></script>`}
        ${assets && `<script src="${assets.app.js}"></script>`}
        ${head.script.toString()}
      </body>
    </html>
  `;
};

Here’s the error message from Chrome : 2016-08-17 6 09 35

When this error happened, webpack usually occur build repeat : 2016-08-17 6 09 55

Error show on <!DOCTYPE html> : 2016-08-17 10 45 55

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 18
  • Comments: 23

Commits related to this issue

Most upvoted comments

I have a same problem fix with added <base href="/" /> into the <head> of my index.html

It’s because html 5 routing … search for it

@CJHJim It seems your router doesn’t find the file from http request, so it return your error page back, if you are using react-router, I suggest you to get realize how it works.

@hemedani May you please elaborate on why and how it works? Did it for me, asking out of curiosity. Thank you.

I am currently having this issue. I tried the base tag and the output: { publicPath: '/' } and they both aren’t working.

@hemedani unbelievable, after i added this line, and it worked as well.

If someone still has the same problem ) I had this code:

app.get("*", (req, res) => {
  res.sendFile(path.join(__dirname, "public", "index.html"));
});
app.use("/", usersRouter);

and it was not working. But then I changed it to:

app.use("/", usersRouter);
app.get("*", (req, res) => {
  res.sendFile(path.join(__dirname, "public", "index.html"));
});

And it finally works! .get(*) should be declare after /use(“/”)

I have a same problem fix with added <base href="/" /> into the <head> of my index.html

@hemedani How do i do this using webpack? Is there a plugin to insert the base tag?

@hemedani same problem too, How to do??

@WellyShen I found this is a “404” problem, the Router needed a javaScript file named ‘1.app.js’ but got a html file, I checked this html file and found that’s my default ‘404’ error page.