简体   繁体   中英

Syntax Error on dynamic import() using webpack and babel

I'm attempting to use a dynamic import to plug our server-generated config.js file into our production build of our app.

I attempt to use it in this way:

import('./config').then(function(config){
    //create a global config variable
    config = config;
})

(for now)

When I run webpack , I get the following error

Module build failed: SyntaxError: Unexpected token, expected { (1:6)

Research has led me to believe that this has something to do with babel-loader perhaps not playing nice with dynamic import, but I thought that had already been "solved" with a newer version.

Solutions seem to potentially have been to install Syntax Dynamic Import or maybe babel-plugin-dynamic-import-node , but it's not clear to me which one or why.

Relevant bits of my package.json :

{
  "name": "app",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "build": "node config/webpack/build.js",
  },
  "dependencies": {
    "amdi18n-loader": "^0.6.2",
    "async": "^2.6.0",
    "babel-core": "^6.26.0",
    "babel-eslint": "^8.2.2",
    "babel-loader": "^7.1.4",
    "babel-plugin-istanbul": "^4.1.6",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.6.1",
    "babel-preset-stage-2": "^6.24.1",
    "babel-register": "^6.26.0",
    "chalk": "^2.3.0",
    "css-loader": "^0.28.10",
    "file-loader": "^1.1.6",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.0.1",
    "less-loader": "^4.0.6",
    "promise-polyfill": "^7.1.0",
    "raw-loader": "^0.5.1",
    "style-loader": "^0.20.2",
    "webpack": "^4.5.0"
  },
  "devDependencies": {
    "autoprefixer": "^7.2.3",
    "chromedriver": "^2.34.0",
    "copy-webpack-plugin": "^4.3.0",
    "cross-env": "^5.1.1",
    "cross-spawn": "^5.1.0",
    "eslint": "^4.13.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.9.0",
    "eslint-plugin-html": "^4.0.1",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "inject-loader": "^3.0.1",
    "madge": "^2.0.0",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.9",
    "rimraf": "^2.6.2",
    "semver": "^5.4.1",
    "shelljs": "^0.7.8",
    "source-map-support": "*",
    "uglifyjs-webpack-plugin": "^1.1.4",
    "url-loader": "^0.6.2",
    "uuid": "^3.1.0",
    "webpack-bundle-analyzer": "^2.9.1",
    "webpack-cli": "^2.0.14",
    "webpack-dev-server": "^3.1.3",
    "webpack-merge": "^4.1.1"
  },
  "engines": {
    "node": ">= 4.0.0",
    "npm": ">= 3.0.0"
  }
}

We split our webpack.conf.js into webpack.base.conf

'use strict';
const path = require('path');
const utils = require('./utils');
const config = require('../');

function resolve(dir) {
    return path.join(__dirname, '../..', dir);
}

const createLintingRule = () => ({
    test: /\.(js|vue)$/,
    loader: 'eslint-loader',
    enforce: 'pre',
    include: [resolve('src'), resolve('test')],
    options: {
        formatter: require('eslint-friendly-formatter'),
        emitWarning: !config.dev.showEslintErrorsInOverlay
    }
});

module.exports = {
    context: path.resolve(__dirname, '../../'),
    entry: {
        app: './public/js/ide.js'
    },
    output: {
        path: config.build.assetsRoot,
        filename: '[name].js',
        publicPath: process.env.NODE_ENV === 'production'
            ? config.build.assetsPublicPath
            : config.dev.assetsPublicPath
    },
    optimization: {
        splitChunks: {
            cacheGroups: {
                commons: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendor',
                    chunks: 'all'
                }
            }
        }
    },
    resolve: {
        extensions: ['.js', '.json', '.less'],
        alias: {
            '@': resolve('public/js'),
        },
        modules: ['less', 'node_modules']
    },
    module: {
        rules: [
            ...(config.dev.useEslint ? [createLintingRule()] : []),
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('public/js'), resolve('test')],
                exclude: [
                    path.resolve(__dirname, "public/js/config.js")
                ],
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]')
                }
            },
            {
                test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('media/[name].[hash:7].[ext]')
                }
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader: 'url-loader',
                options: {
                    limit: 10000,
                    name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.less$/,
                use: [{
                    loader: "style-loader" // creates style nodes from JS strings
                }, {
                    loader: "css-loader" // translates CSS into CommonJS
                }, {
                    loader: "less-loader" // compiles Less to CSS
                }]
            },
            {
                test: /\.hbs$/,
                use: ['raw-loader']
            }
        ]
    },
    node: {
        setImmediate: false,
        dgram: 'empty',
        fs: 'empty',
        net: 'empty',
        tls: 'empty',
        child_process: 'empty'
    }
};

...and webpack.prod.conf.js

const webpackConfig = merge(baseWebpackConfig, {
    devtool: config.build.productionSourceMap ? config.build.devtool : false,
    output: {
        path: config.build.assetsRoot,
        filename: utils.assetsPath('js/[name].[chunkhash].js'),
        chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': env
        }),
        new UglifyJsPlugin({
            uglifyOptions: {
                compress: {
                    warnings: false
                }
            },
            sourceMap: config.build.productionSourceMap,
            parallel: true
        }),
        new OptimizeCSSPlugin({
            cssProcessorOptions: config.build.productionSourceMap
                ? { safe: true, map: { inline: false } }
                : { safe: true }
        }),
        new HtmlWebpackPlugin({
            filename: process.env.NODE_ENV === 'testing'
                ? 'public/html/ide.html'
                : config.build.index,
            template: 'public/html/ide.html',
            inject: true,
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
            },
            chunksSortMode: 'dependency'
        }),
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery"
        }),
        new webpack.HashedModuleIdsPlugin(),
        new webpack.optimize.ModuleConcatenationPlugin(),
        new CopyWebpackPlugin([
            {
                from: path.resolve(__dirname, '../../public/fonts'),
                to: config.build.assetsFonts,
            },
            {
                from: path.resolve(__dirname, '../../public/img'),
                to: config.build.assetsImg,
            }
        ])
    ]
});

In 1 , a user indicated that babel-loader should "just work" with dynamic imports now, so I'm not sure what I'm missing. Something out of date? Something weird in my config?

There are a couple of things you need to do.

First, like some people in the comments mentioned, you need to use a different variable name for your config. Also, if you use import this way, the object you get is a ES6 exported object, so your data will be in default

import('./config').then(function(localConfig){
    //create a global config variable
    config = localConfig.default;
})

Next, the error you are getting is the result of babel not using the correct preset. You need to be at least on stage-2 for this to work.

{
    test: /\.js$/,
    loader: 'babel-loader',
    options: {
        presets: ['es2015','stage-2']
    }
    include: [resolve('public/js'), resolve('test')],
    exclude: [
        path.resolve(__dirname, "public/js/config.js")
    ],
},

This will solve your error, but is still not what you want. All that is happening now is that webpack will split your config file into its own chunk, making a new http request on runtime to get this file. But it will not use the config file that is on the server, but the generated chunk from the build.
To load the actual config that you have on the server on runtime, you need to make a normal http request in your app.
Here is a good stackoverflow answer about this.
https://stackoverflow.com/a/36725115/9083959

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