Skip to content
Snippets Groups Projects
webpack.config.js 9.15 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* eslint-env node */
    /* eslint-disable import/no-commonjs */
    
    
    require("babel-register");
    require("babel-polyfill");
    
    Tom Robinson's avatar
    Tom Robinson committed
    var webpack = require('webpack');
    var webpackPostcssTools = require('webpack-postcss-tools');
    
    var ExtractTextPlugin = require('extract-text-webpack-plugin');
    
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    
    var HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
    
    var UnusedFilesWebpackPlugin = require("unused-files-webpack-plugin").default;
    
    var BannerWebpackPlugin = require('banner-webpack-plugin');
    
    Tom Robinson's avatar
    Tom Robinson committed
    var _ = require('underscore');
    var glob = require('glob');
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
    Tom Robinson's avatar
    Tom Robinson committed
    var chevrotain = require("chevrotain");
    var allTokens = require("./frontend/src/metabase/lib/expressions/tokens").allTokens;
    
    
    function hasArg(arg) {
        var regex = new RegExp("^" + ((arg.length === 2) ? ("-\\w*"+arg[1]+"\\w*") : (arg)) + "$");
        return process.argv.filter(regex.test.bind(regex)).length > 0;
    }
    
    
    var SRC_PATH = __dirname + '/frontend/src/metabase';
    
    var BUILD_PATH = __dirname + '/resources/frontend_client';
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
    // default NODE_ENV to development
    var NODE_ENV = process.env["NODE_ENV"] || "development";
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
    Tom Robinson's avatar
    Tom Robinson committed
    // Need to scan the CSS files for variable and custom media used across files
    // NOTE: this requires "webpack -w" (watch mode) to be restarted when variables change :(
    
    var IS_WATCHING = hasArg("-w") || hasArg("--watch");
    if (IS_WATCHING) {
        process.stderr.write("Warning: in webpack watch mode you must restart webpack if you change any CSS variables or custom media queries\n");
    
    // Babel:
    var BABEL_CONFIG = {
    
    Tom Robinson's avatar
    Tom Robinson committed
        cacheDirectory: ".babel_cache"
    
    // Build mapping of CSS variables
    var CSS_SRC = glob.sync(SRC_PATH + '/css/**/*.css');
    
    var CSS_MAPS = { vars: {}, media: {}, selector: {} };
    
    Tom Robinson's avatar
    Tom Robinson committed
    CSS_SRC.map(webpackPostcssTools.makeVarMap).forEach(function(map) {
    
        for (var name in CSS_MAPS) _.extend(CSS_MAPS[name], map[name]);
    
    Tom Robinson's avatar
    Tom Robinson committed
    });
    
    
    // CSS Next:
    
    var CSSNEXT_CONFIG = {
        features: {
            // pass in the variables and custom media we scanned for before
            customProperties: { variables: CSS_MAPS.vars },
            customMedia: { extensions: CSS_MAPS.media }
        },
        import: {
            path: ['resources/frontend_client/app/css']
        },
        compress: false
    };
    
        localIdentName: NODE_ENV !== "production" ?
            "[name]__[local]___[hash:base64:5]" :
            "[hash:base64:5]",
    
        restructuring: false,
    
    Tom Robinson's avatar
    Tom Robinson committed
    var config = module.exports = {
    
    Tom Robinson's avatar
    Tom Robinson committed
        // output a bundle for the app JS and a bundle for styles
        // eventually we should have multiple (single file) entry points for various pieces of the app to enable code splitting
    
    Tom Robinson's avatar
    Tom Robinson committed
        entry: {
    
            "app-main": './app-main.js',
            "app-public": './app-public.js',
            "app-embed": './app-embed.js',
    
            styles: './css/index.css',
    
    Tom Robinson's avatar
    Tom Robinson committed
        },
    
    
    Tom Robinson's avatar
    Tom Robinson committed
        // output to "dist"
    
    Tom Robinson's avatar
    Tom Robinson committed
        output: {
    
            path: BUILD_PATH + '/app/dist',
    
            // NOTE: the filename on disk won't include "?[chunkhash]" but the URL in index.html generated by HtmlWebpackPlugin will:
    
            filename: '[name].bundle.js?[hash]',
    
            publicPath: '/app/dist/'
    
    Tom Robinson's avatar
    Tom Robinson committed
        },
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
    Tom Robinson's avatar
    Tom Robinson committed
        module: {
            loaders: [
    
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules/,
                    loader: "babel",
                    query: BABEL_CONFIG
                },
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node_modules|\.spec\.js/,
                    loader: 'eslint'
                },
    
    Tom Robinson's avatar
    Tom Robinson committed
                {
    
                    test: /\.(eot|woff2?|ttf|svg|png)$/,
    
    Tom Robinson's avatar
    Tom Robinson committed
                    loader: "file-loader"
                },
    
                {
                    test: /\.json$/,
                    loader: "json-loader"
                },
    
                {
                    test: /\.css$/,
    
                    loader: ExtractTextPlugin.extract("style-loader", "css-loader?" + JSON.stringify(CSS_CONFIG) + "!postcss-loader")
    
    Tom Robinson's avatar
    Tom Robinson committed
            ]
        },
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
        resolve: {
    
            extensions: ["", ".webpack.js", ".web.js", ".js", ".jsx", ".css"],
    
                'style':                SRC_PATH + '/css/core/index.css',
    
                'ace':                  __dirname + '/node_modules/ace-builds/src-min-noconflict',
    
    Tom Robinson's avatar
    Tom Robinson committed
        plugins: [
    
            new UnusedFilesWebpackPlugin({
                globOptions: {
                    ignore: [
    
                        "**/types/*.js",
                        "**/*.spec.*"
    
    Tom Robinson's avatar
    Tom Robinson committed
            // Extracts initial CSS into a standard stylesheet that can be loaded in parallel with JavaScript
    
            // NOTE: the filename on disk won't include "?[chunkhash]" but the URL in index.html generated by HtmlWebpackPlugin will:
    
            new ExtractTextPlugin('[name].bundle.css?[contenthash]'),
            new HtmlWebpackPlugin({
    
                chunks: ["app-main", "styles"],
    
                template: __dirname + '/resources/frontend_client/index_template.html',
    
            }),
            new HtmlWebpackPlugin({
                filename: '../../public.html',
    
                chunks: ["app-public", "styles"],
                template: __dirname + '/resources/frontend_client/index_template.html',
    
            }),
            new HtmlWebpackPlugin({
                filename: '../../embed.html',
                chunks: ["app-embed", "styles"],
    
                template: __dirname + '/resources/frontend_client/index_template.html',
    
                inject: 'head',
                alwaysWriteToDisk: true,
            }),
            new HtmlWebpackHarddiskPlugin({
                outputPath: __dirname + '/resources/frontend_client/app/dist'
    
                'process.env': {
                    NODE_ENV: JSON.stringify(NODE_ENV)
                }
    
            }),
            new BannerWebpackPlugin({
                chunks: {
                    'app-main': {
                        beforeContent: "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE.txt', which is part of this source code package.\n */\n",
                    },
                    'app-public': {
                        beforeContent: "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE.txt', which is part of this source code package.\n */\n",
                    },
                    'app-embed': {
                        beforeContent: "/*\n* This file is subject to the terms and conditions defined in\n * file 'LICENSE-EMBEDDING.txt', which is part of this source code package.\n */\n",
                    },
                }
            }),
    
    Tom Robinson's avatar
    Tom Robinson committed
        ],
    
    Tom Robinson's avatar
    Tom Robinson committed
    
    
        postcss: function (webpack) {
            return [
    
                require("postcss-import")(),
    
                require("postcss-url")(),
                require("postcss-cssnext")(CSSNEXT_CONFIG)
            ]
        }
    
        // suffixing with ".hot" allows us to run both `yarn run build-hot` and `yarn run test` or `yarn run test-watch` simultaneously
    
        config.output.filename = "[name].hot.bundle.js?[hash]";
    
        // point the publicPath (inlined in index.html by HtmlWebpackPlugin) to the hot-reloading server
    
    Tom Robinson's avatar
    Tom Robinson committed
        config.output.publicPath = "http://localhost:8080" + config.output.publicPath;
    
        config.module.loaders.unshift({
            test: /\.jsx$/,
            exclude: /node_modules/,
            loaders: ['react-hot', 'babel?'+JSON.stringify(BABEL_CONFIG)]
        });
    
        // disable ExtractTextPlugin
    
        config.module.loaders[config.module.loaders.length - 1].loader = "style-loader!css-loader?" + JSON.stringify(CSS_CONFIG) + "!postcss-loader"
    
        config.devServer = {
            hot: true,
            inline: true,
            contentBase: "frontend"
    
            // if webpack doesn't reload UI after code change in development
            // watchOptions: {
            //     aggregateTimeout: 300,
            //     poll: 1000
            // }
    
            // if you want to reduce stats noise
            // stats: 'minimal' // values: none, errors-only, minimal, normal, verbose
    
    Tom Robinson's avatar
    Tom Robinson committed
        config.plugins.unshift(
    
            new webpack.NoErrorsPlugin(),
            new webpack.HotModuleReplacementPlugin()
    
    if (NODE_ENV !== "production") {
    
        // replace minified files with un-minified versions
    
    Tom Robinson's avatar
    Tom Robinson committed
        for (var name in config.resolve.alias) {
            var minified = config.resolve.alias[name];
    
            var unminified = minified.replace(/[.-\/]min\b/g, '');
    
            if (minified !== unminified && fs.existsSync(unminified)) {
    
    Tom Robinson's avatar
    Tom Robinson committed
                config.resolve.alias[name] = unminified;
    
        // enable "cheap" source maps in hot or watch mode since re-build speed overhead is < 1 second
    
        config.devtool = "cheap-module-source-map";
    
    
        // works with breakpoints
        // config.devtool = "inline-source-map"
    
        // helps with source maps
        config.output.devtoolModuleFilenameTemplate = '[absolute-resource-path]';
        config.output.pathinfo = true;
    
    } else {
        // this is required to ensure we don't minify Chevrotain token identifiers
        // https://github.com/SAP/chevrotain/tree/master/examples/parser/minification
        config.plugins.push(new webpack.optimize.UglifyJsPlugin({
            mangle: {
    
    Tom Robinson's avatar
    Tom Robinson committed
                except: allTokens.map(function(currTok) {
                    return chevrotain.tokenName(currTok);
                })
    
        config.devtool = "source-map";