简体   繁体   中英

HMR - Update failed: SyntaxError: Unexpected token < in JSON at position 0

I'm working in a Elmish application. I put in place SSR (server-side rendering) for the index page only as I need some dynamically generated initial state at the start of the application. Since the Hot module replacement is not working and it is throwing the following warning:

[HMR] Update failed: SyntaxError: Unexpected token < in JSON at position 0
at JSON.parse (<anonymous>)
at XMLHttpRequest.request.onreadystatechange

I know the config for webpack-devserver is wrong as the onreadystatechange is retrieving the index page in html rather that the new app file (?) in json. But reading the documentation didn't help me much as I'm not sure how to prevent request like /en-EN/4b1807ffe818fe814be7.hot-update.json to be proxied to the back-end.

How can I get HRM to work again?

my webpack.config looks like this:

var path = require('path');
var webpack = require('webpack');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');

var CONFIG = {
    // The tags to include the generated JS and CSS will be automatically injected in the HTML template
    // See https://github.com/jantimon/html-webpack-plugin
    fsharpEntry: './src/UI/Client/Client.fsproj',
    lessEntry: './src/UI/Client/style.less',
    outputDir: './src/UI/Client/deploy',
    assetsDir: './src/UI/Client/public',
    devServerPort: 8080,
    // When using webpack-dev-server, you may need to redirect some calls
    // to a external API server. See https://webpack.js.org/configuration/dev-server/#devserver-proxy
    devServerProxy: {
        '/': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            changeOrigin: true
        },
        // redirect requests that start with /api/* to the server on port 8085
        '/api/*': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            changeOrigin: true
        },
        // redirect websocket requests that start with /socket/* to the server on the port 8085
        '/socket/*': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            ws: true
        }
    },
    // Use babel-preset-env to generate JS compatible with most-used browsers.
    // More info at https://babeljs.io/docs/en/next/babel-preset-env.html
    babel: {
        presets: [
            ['@babel/preset-env', {
                modules: false,
                // This adds polyfills when needed. Requires core-js dependency.
                // See https://babeljs.io/docs/en/babel-preset-env#usebuiltins
                useBuiltIns: 'usage',
                corejs: 3
            }]
        ],
    }
}

// If we're running the webpack-dev-server, assume we're in development mode
var isProduction = !process.argv.find(v => v.indexOf('webpack-dev-server') !== -1);
console.log('Bundling for ' + (isProduction ? 'production' : 'development') + '...');

module.exports = _ => {

    var commonPlugins = [];

    return {
        // In development, split the JavaScript and CSS files in order to
        // have a faster HMR support. In production bundle styles together
        // with the code because the MiniCssExtractPlugin will extract the
        // CSS in a separate files.
        entry: isProduction ? {
            app: [resolve(CONFIG.fsharpEntry), resolve(CONFIG.lessEntry)]
        } : {
                app: [resolve(CONFIG.fsharpEntry)],
                style: [resolve(CONFIG.lessEntry)]
            },
        // Add a hash to the output file name in production
        // to prevent browser caching if code changes
        output: {
            path: resolve(CONFIG.outputDir),
            filename: isProduction ? '[name].[hash].js' : '[name].js'
        },
        mode: isProduction ? 'production' : 'development',
        devtool: isProduction ? 'source-map' : 'eval-source-map',
        optimization: {
            splitChunks: {
                chunks: 'all'
            },
        },
        // Besides the HtmlPlugin, we use the following plugins:
        // PRODUCTION
        //      - MiniCssExtractPlugin: Extracts CSS from bundle to a different file
        //          To minify CSS, see https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production
        //      - CopyWebpackPlugin: Copies static assets to output directory
        // DEVELOPMENT
        //      - HotModuleReplacementPlugin: Enables hot reloading when code changes without refreshing
        plugins: isProduction ?
            commonPlugins.concat([
                new MiniCssExtractPlugin({ filename: 'style.[hash].css' }),
                new CopyWebpackPlugin({ patterns : [{ from: resolve(CONFIG.assetsDir) }]}),
            ])
            : commonPlugins.concat([
                new webpack.HotModuleReplacementPlugin(),
            ]),
        resolve: {
            // See https://github.com/fable-compiler/Fable/issues/1490
            symlinks: false
        },
        // Configuration for webpack-dev-server
        devServer: {
            publicPath: '/',
            contentBase: resolve(CONFIG.assetsDir),
            host: '0.0.0.0',
            port: CONFIG.devServerPort,
            proxy: CONFIG.devServerProxy,
            hot: true,
            inline: true,
            historyApiFallback: true
        },
        // - fable-loader: transforms F# into JS
        // - babel-loader: transforms JS to old syntax (compatible with old browsers)
        // - sass-loaders: transforms SASS/SCSS into JS
        // - file-loader: Moves files referenced in the code (fonts, images) into output folder
        module: {
            rules: [
                {
                    test: /\.fs(x|proj)?$/,
                    use: {
                        loader: 'fable-loader',
                        options: {
                            babel: CONFIG.babel
                        }
                    }
                },
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: {
                        loader: 'babel-loader',
                        options: CONFIG.babel
                    },
                },
                {
                    test: /\.(le|c)ss$/,
                    use: [
                        isProduction
                            ? MiniCssExtractPlugin.loader
                            : 'style-loader',
                        'css-loader',
                        {
                            loader: 'less-loader',
                            options: { implementation: require('less') }
                        }
                    ]
                },
                {
                    test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?.*)?$/,
                    use: ['file-loader']
                }
            ]
        }
    };
};

function resolve(filePath) {
    console.log("resolve filepath: " + filePath);
    var resolved = path.isAbsolute(filePath) ? filePath : path.join(__dirname, filePath);
    console.log("resolved: " + resolved);
    return resolved;
}

my server relevant part looks like this:

// Fable.Remoting api (as giraffe HttpHandler)
let createApi config httpClient translator=
    Remoting.createApi()
    |> Remoting.withErrorHandler errorHandler
    |> Remoting.withRouteBuilder Route.builder
    |> Remoting.fromValue (api config httpClient translator)
    |> Remoting.buildHttpHandler

let printRequestPath : HttpHandler =
    fun next ctx ->
        printfn "request path: %O" ctx.Request.Path
        next ctx

let webApp config httpClient translator =
    choose [
        GET >=> route "/" >=> Index.indexHandler config
        createApi config httpClient translator
        GET >=> Index.indexHandler config // default every GET route to index so client application handles it. TODO: there is a better way? rewriting on prod? on webpack-devserver historyapifallback?
    ]

well, writing the question I thought of something, I was tired lastnight I thing. So I read the documentation again and found a bypass option which worked.

I change the proxy config like this:

devServerProxy: {
    '/': {
            target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
            changeOrigin: true,
            bypass: function(req, res, proxyOptions) {
                if (req.path.indexOf('.hot-update.js') !== -1) {
                    var lastSlashIndex = req.path.lastIndexOf('/');
                    return req.path.substr(lastSlashIndex);
                }
                return null;
            }
        }
// rest as before ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM