| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 | const HtmlWebpackPlugin = require('html-webpack-plugin');const CopyPlugin = require("copy-webpack-plugin");const HtmlMinimizerPlugin = require("html-minimizer-webpack-plugin");const { CleanWebpackPlugin } = require("clean-webpack-plugin");const MiniCssExtractPlugin = require("mini-css-extract-plugin");const CompressionPlugin = require("compression-webpack-plugin");const TerserPlugin = require("terser-webpack-plugin");const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");const webpack = require("webpack");const path = require("path");const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;const globSync = require("glob").sync;const glob = require('glob');const { merge } = require('webpack-merge');const devserver = require('./webpack/webpack.dev.js');const fs = require('fs');const zlib = require("zlib");const PurgeCSSPlugin = require('purgecss-webpack-plugin')const whitelister = require('purgecss-whitelister');const PATHS = {  src: path.join(__dirname, 'src')}class BuildEventsHook {  constructor(name, fn, stage = 'afterEmit') {    this.name = name;    this.stage = stage;    this.function = fn;  }  apply(compiler) {    compiler.hooks[this.stage].tap(this.name, this.function);  }}module.exports = (env, options) => (  merge(    env.WEBPACK_SERVE ?  devserver : {},    env.ANALYZE_SIZE?{ plugins: [ new BundleAnalyzerPlugin(      {        analyzerMode: 'static',        generateStatsFile: true,        statsFilename: 'stats.json',      }    )      ]}:{},        {      entry:       {          index: './src/index.ts'      },      devtool:"source-map",      module: {        rules: [          {            test: /\.ejs$/,            loader: 'ejs-loader',            options: {              variable: 'data',              interpolate : '\\{\\{(.+?)\\}\\}',              evaluate : '\\[\\[(.+?)\\]\\]'            }          },                    {            test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,            use: [              {                loader: 'file-loader',                options: {                  name: '[name].[ext]',                  outputPath: 'fonts/'                }              }            ]          },           // {          //   test: /\.s[ac]ss$/i,                      //   use:  [{          //     loader: 'style-loader', // inject CSS to page          //   },          //      {          //         loader: MiniCssExtractPlugin.loader,          //         options: {          //           publicPath: "../",          //         },          //       },          //     "css-loader",          //     {          //       loader: "postcss-loader",          //       options: {          //         postcssOptions: {          //           plugins: [["autoprefixer"]],          //         },          //       },          //     },          //     "sass-loader",          //   ]          // },          {            test: /\.(scss)$/,            use: [                            {                loader: MiniCssExtractPlugin.loader,                options: {                  publicPath: "../",                },              },            //   {            //   // inject CSS to page            //   loader: 'style-loader'            // },             {              // translates CSS into CommonJS modules              loader: 'css-loader'            },                            {              // Run postcss actions              loader: 'postcss-loader',              options: {                // `postcssOptions` is needed for postcss 8.x;                // if you use postcss 7.x skip the key                postcssOptions: {                  // postcss plugins, can be exported to postcss.config.js                  plugins: function () {                    return [                      require('autoprefixer')                    ];                  }                }              }            }, {              // compiles Sass to CSS              loader: 'sass-loader'            }]          },                    {            test: /\.js$/,            exclude: /(node_modules|bower_components)/,            use: {              loader: "babel-loader",              options: {                presets: ["@babel/preset-env"],                plugins: ['@babel/plugin-transform-runtime']              },            },          },          {            test: /\.(jpe?g|png|gif|svg)$/i,            type: "asset",          },           // {          //   test: /\.html$/i,          //   type: "asset/resource",          // },        {          test: /\.html$/i,          loader: "html-loader",          options: {            minimize: true,                      }        },          {            test: /\.tsx?$/,            use: 'ts-loader',            exclude: /node_modules/,          },                            ],      },      plugins: [        new HtmlWebpackPlugin({          title: 'SqueezeESP32',          template: './src/index.ejs',          filename: 'index.html',          inject: 'body',          minify: {            html5                          : true,            collapseWhitespace             : true,            minifyCSS                      : true,            minifyJS                       : true,            minifyURLs                     : false,            removeAttributeQuotes          : true,            removeComments                 : true, // false for Vue SSR to find app placeholder            removeEmptyAttributes          : true,            removeOptionalTags             : true,            removeRedundantAttributes      : true,            removeScriptTypeAttributes     : true,            removeStyleLinkTypeAttributese : true,            useShortDoctype                : true          },          favicon: "./src/assets/images/favicon-32x32.png",          excludeChunks: ['test'],      }),      // new CompressionPlugin({      //   test: /\.(js|css|html|svg)$/,      //   //filename: '[path].br[query]',      //   filename: "[path][base].br",      //   algorithm: 'brotliCompress',      //   compressionOptions: { level: 11 },      //   threshold: 100,      //   minRatio: 0.8,      //   deleteOriginalAssets: false      // }),        new MiniCssExtractPlugin({          filename: "css/[name].[contenthash].css",        }),        new PurgeCSSPlugin({          keyframes: false,          paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, {            nodir: true          }),          whitelist: whitelister('bootstrap/dist/css/bootstrap.css')        }),        new webpack.ProvidePlugin({          $: "jquery",//          jQuery: "jquery",  //        "window.jQuery": "jquery",    //      Popper: ["popper.js", "default"],          // Util: "exports-loader?Util!bootstrap/js/dist/util",          // Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",        }),        new CompressionPlugin({          //filename: '[path].gz[query]',          test: /\.js$|\.css$|\.html$/,          filename: "[path][base].gz",          algorithm: 'gzip',                    threshold: 100,          minRatio: 0.8,      }),              new BuildEventsHook('Update C App',           function (stats, arguments) {            if (options.mode !== "production") return;            let buildRootPath = path.join(process.cwd(),'..','..','..');            let wifiManagerPath=glob.sync(path.join(buildRootPath,'components/**/wifi-manager*'))[0];            let buildCRootPath=glob.sync(buildRootPath)[0];            fs.appendFileSync('./dist/index.html.gz',               zlib.gzipSync(fs.readFileSync('./dist/index.html'),               {                chunckSize: 65536,                level: zlib.constants.Z_BEST_COMPRESSION             }));                        var getDirectories = function (src, callback) {              var searchPath = path.posix.join(src, '/**/*(*.gz|favicon-32x32.png)');              console.log(`Post build: Getting file list from ${searchPath}`);              glob(searchPath, callback);            };            var cleanUpPath = path.posix.join(buildCRootPath, '/build/*.S');            console.log(`Post build: Cleaning up previous builds in ${cleanUpPath}`);            glob(cleanUpPath, function (err, list) {              if (err) {                console.error('Error', err);              } else {                list.forEach(fileName => {                  try {                    console.log(`Post build: Purging old binary file ${fileName} from C project.`);                    fs.unlinkSync(fileName)                    //file removed                  } catch (ferr) {                    console.error(ferr)                  }                });              }            },            'afterEmit'            );            console.log('Generating C include files from webpack build output');            getDirectories('./dist', function (err, list) {              console.log(`Post build: found ${list.length} files. Relative path: ${wifiManagerPath}.`);              if (err) {                console.log('Error', err);              } else {                              let exportDefHead =`/***********************************webpack_headers${arguments[1]}***********************************/#pragma once#include <inttypes.h>extern const char * resource_lookups[];extern const uint8_t * resource_map_start[];extern const uint8_t * resource_map_end[];`;                let exportDef = '// Automatically generated. Do not edit manually!.\n' +                  '#include <inttypes.h>\n';                let lookupDef = 'const char * resource_lookups[] = {\n';                let lookupMapStart = 'const uint8_t * resource_map_start[] = {\n';                let lookupMapEnd = 'const uint8_t * resource_map_end[] = {\n';                let cMake='';                                list.forEach(foundFile => {                  let exportName = path.basename(foundFile).replace(/[\. \-]/gm, '_');                  //take the full path of the file and make it relative to the build directory                  let cmakeFileName = path.posix.relative(wifiManagerPath,glob.sync(path.resolve(foundFile))[0]);                  let httpRelativePath=path.posix.join('/',path.posix.relative('dist',foundFile));                  exportDef += `extern const uint8_t _${exportName}_start[] asm("_binary_${exportName}_start");\nextern const uint8_t _${exportName}_end[] asm("_binary_${exportName}_end");\n`;                  lookupDef += `\t"${httpRelativePath}",\n`;                  lookupMapStart += '\t_' + exportName + '_start,\n';                  lookupMapEnd += '\t_' + exportName + '_end,\n';                  cMake += `target_add_binary_data( __idf_wifi-manager ${cmakeFileName} BINARY)\n`;                  console.log(`Post build: adding cmake file reference to ${cmakeFileName} from C project, with web path ${httpRelativePath}.`);                });                lookupDef += '""\n};\n';                lookupMapStart = lookupMapStart.substring(0, lookupMapStart.length - 2) + '\n};\n';                lookupMapEnd = lookupMapEnd.substring(0, lookupMapEnd.length - 2) + '\n};\n';                try {                  fs.writeFileSync('webapp.cmake', cMake);                  fs.writeFileSync('webpack.c', exportDef + lookupDef + lookupMapStart + lookupMapEnd);                  fs.writeFileSync('webpack.h', exportDefHead);                  //file written successfully                } catch (e) {                  console.error(e);                }              }            });            console.log('Post build completed.');          })      ],      optimization: {        minimize: true,        providedExports: true,        usedExports: true,        minimizer: [                   new TerserPlugin({            terserOptions: {              format: {                  comments: false,              },            },            extractComments: false,          // enable parallel running            parallel: true,          }),          new HtmlMinimizerPlugin({            minimizerOptions: {              removeComments: true,              removeOptionalTags: true,            }          }          ),          new CssMinimizerPlugin(),          new ImageMinimizerPlugin({            minimizer: {              implementation: ImageMinimizerPlugin.imageminMinify,              options: {                // Lossless optimization with custom option                // Feel free to experiment with options for better result for you                plugins: [                  ["gifsicle", { interlaced: true }],                  ["jpegtran", { progressive: true }],                  ["optipng", { optimizationLevel: 5 }],                  // Svgo configuration here https://github.com/svg/svgo#configuration                  [                    "svgo",                    {                      plugins: [                       {                          name: 'preset-default',                         params: {                          overrides: {                            // customize default plugin options                            inlineStyles: {                              onlyMatchedOnce: false,                            },                                              // or disable plugins                            removeDoctype: false,                          },                        },                      }                      ],                    },                  ],                ],              },            },          }),                    ],        splitChunks: {          cacheGroups: {              vendor: {                  name: "node_vendors",                  test: /[\\/]node_modules[\\/]/,                  chunks: "all",              }          }      }            },      //   output: {      //     filename: "[name].js",      //     path: path.resolve(__dirname, "dist"),      //     publicPath: "",      //   },      resolve: {        extensions: ['.tsx', '.ts', '.js', '.ejs' ],      },      output: {        path: path.resolve(__dirname, 'dist'),        filename: './js/[name].[fullhash:6].bundle.js',        clean: true      },    }  ));
 |