| const path = require('path') | = | const path = require('path') |
| const glob = require('glob') | const glob = require('glob') | |
| -+ | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') | |
| = | ||
| // ------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------- | |
| // Config | // Config | |
| const conf = (() => { | const conf = (() => { | |
| const _conf = require('./build-config') | const _conf = require('./build-config') | |
| return require('deepmerge').all([{}, _conf.base || {}, _conf[process.env.NODE_ENV] || {}]) | return require('deepmerge').all([{}, _conf.base || {}, _conf[process.env.NODE_ENV] || {}]) | |
| })() | })() | |
| conf.buildPath = path.resolve(__dirname, conf.buildPath) | conf.buildPath = path.resolve(__dirname, conf.buildPath) | |
| // ------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------- | |
| // Modules | <> | |
| // NPM packages to transpile | ||
| const webpack = require('webpack') | ||
| const StringReplacePlugin = require('string-replace-webpack-plugin') | const TRANSPILE_PACKAGES = [ | |
| 'bootstrap', | ||
| 'bootstrap-slider', | ||
| 'popper.js', | ||
| 'bootstrap-table', | ||
| 'shepherd.js', | ||
| 'flot', | ||
| ] | ||
| const UglifyJsPlugin = require('uglifyjs-webpack-plugin') | ||
| const packageRejex = package => `(?:\\\\|\\/)${package}(?:\\\\|\\/).+?\\.js$` | ||
| = | ||
| // ------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------- | |
| // Custom template | <> | |
| // Build config | ||
| const ConcatSource = require('webpack-sources').ConcatSource | ||
| const collectEntries = () => { | ||
| class CustomTemplatePlugin { | ||
| apply(compilation) { | ||
| const { mainTemplate, chunkTemplate } = compilation; | ||
| const fileList = glob.sync(`!(${conf.exclude.join('|')})/**/!(_)*.@(js|es6)`) || [] | ||
| const onRenderWithEntry = (source, chunk, hash) => { | ||
| return new ConcatSource(` | ||
| (function(r,f) { | ||
| var a=f(); | ||
| if(typeof a!=='object')return; | ||
| var e=[typeof module==='object'&&typeof module.exports==='object'?module.exports:null,typeof window!=='undefined'?window:null,r&&r!==window?r:null]; | ||
| for(var i in a){e[0]&&(e[0][i]=a[i]);e[1]&&i!=='__esModule'&&(e[1][i] = a[i]);e[2]&&(e[2][i]=a[i]);} | ||
| })(this,function(){ | ||
| return `, source, `; | ||
| });`) | ||
| }; | ||
| return fileList.reduce((entries, file) => { | ||
| for (const template of [mainTemplate, chunkTemplate]) { | ||
| template.hooks.renderWithEntry.tap( | ||
| 'CustomTemplatePlugin', | ||
| onRenderWithEntry | ||
| ); | ||
| } | ||
| const filePath = file.replace(/\\/g, '/') | ||
| mainTemplate.hooks.globalHashPaths.tap("CustomTemplatePlugin", paths => { | ||
| return paths; | ||
| }); | ||
| return { ...entries, [filePath.replace(/\.(?:js|es6)$/, '')]: `./${filePath}` } | ||
| mainTemplate.hooks.hash.tap("CustomTemplatePlugin", hash => { | ||
| hash.update('CustomTemplatePlugin'); | ||
| hash.update('2'); | ||
| }); | }, {}) | |
| } | } | |
| } | ||
| const babelLoader = () => ({ | ||
| class CustomLibraryTemplatePlugin { | loader: 'babel-loader', | |
| apply(compiler) { | options: { | |
| compiler.hooks.thisCompilation.tap('CustomLibraryTemplatePlugin', compilation => { | presets: [['@babel/preset-env', { targets: 'last 2 versions, ie >= 10' }]], | |
| new CustomTemplatePlugin().apply(compilation); | plugins: ['@babel/plugin-transform-destructuring', '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-transform-template-literals'], | |
| }); | babelrc: false | |
| } | } | |
| } | }) | |
| = | ||
| // ------------------------------------------------------------------------------- | +- | |
| // Build config | ||
| const webpackConfig = { | = | const webpackConfig = { |
| target: 'web', | +- | |
| mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', | = | mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', |
| <> | ||
| // Collect entries | entry: collectEntries(), | |
| entry: (glob.sync(`!(${conf.exclude.join('|')})/**/!(_)*.@(js|es6)`) || []) | ||
| .reduce((entries, file) => { | ||
| const filePath = file.replace(/\\/g, '/') | ||
| entries[filePath.replace(/\.(?:js|es6)$/, '')] = `./${filePath}` | ||
| return entries | ||
| }, {}), | ||
| = | ||
| output: { | <> | output: { |
| path: conf.buildPath, | path: conf.buildPath, | |
| filename: '[name].js' | filename: '[name].js', | |
| libraryTarget: 'window' | ||
| }, | }, | |
| = | ||
| module: { | module: { | |
| +- | ||
| rules: [{ | = | rules: [{ |
| test: /(?:\.es6|\.es6\.js)$|(?:node_modules(?:\\|\/)bootstrap(?:\\|\/)js(?:\\|\/).+?\.js$)|(?:bootstrap-sweetalert(?:\/|\\)dev.+?\.js)$|(?:bootstrap-slider\.js)$|(?:popper\.js)$|(?:bootstrap-table(?:\\|\/).+?\.js$)|(?:shepherd\.js)|(?:fullcalendar\.js)|(?:sweetalert2\.js)/, | <> | // Transpile sources |
| use: [ | test: /\.es6$|\.js$/, | |
| { | exclude: [path.resolve(__dirname, 'node_modules')], | |
| loader: 'babel-loader', | use: [babelLoader()] | |
| options: { | ||
| presets: [['@babel/preset-env', { | ||
| targets: 'last 2 versions, ie >= 10' | ||
| }]], | }, { | |
| plugins: ['@babel/plugin-transform-destructuring', '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-transform-template-literals'], | // Pranspile required packages | |
| test: new RegExp(`(?:${TRANSPILE_PACKAGES.map(packageRejex).join(')|(?:')})`), | ||
| include: [path.resolve(__dirname, 'node_modules')], | ||
| babelrc: false | use: [babelLoader()] | |
| } | ||
| } | ||
| ] | ||
| }, { | = | }, { |
| test: /\.css$/, | <> | test: /\.css$/, |
| use: [{ | use: [ | |
| loader: 'style-loader', | { loader: 'style-loader' }, | |
| options: { | ||
| hmr: false | ||
| } | ||
| }, { | ||
| loader: 'css-loader' | { loader: 'css-loader' } | |
| }] | ] | |
| }, { | }, { | |
| test: /\.scss$/, | test: /\.scss$/, | |
| use: [{ | use: [ | |
| loader: 'style-loader', | { loader: 'style-loader' }, | |
| options: { | ||
| hmr: false | ||
| } | ||
| }, { | ||
| loader: 'css-loader' | { loader: 'css-loader' }, | |
| }, { | ||
| loader: 'sass-loader' | { loader: 'sass-loader' } | |
| }] | ] | |
| }, { | }, { | |
| test: /\.html$/, | test: /\.html$/, | |
| use: [{ | use: [{ | |
| loader: 'html-loader', | loader: 'html-loader', | |
| options: { | ||
| minimize: true | options: { minimize: true } | |
| } | ||
| }] | }] | |
| }, { // Remove imports | ||
| test: /node_modules(?:\\|\/)bootstrap(?:\\|\/)js(?:\\|\/)src(?:\\|\/)\w+\.js$/, | ||
| use: { | ||
| loader: StringReplacePlugin.replace({ | ||
| replacements: [{ | ||
| pattern: /import.+?from\s+['"](.+?)['"];?\n/ig, | ||
| replacement: (m, $1) => $1.toLowerCase() !== './tooltip' ? '' : 'import Tooltip from \'./tooltip\'\n' | ||
| }] | ||
| }) | ||
| } | ||
| }, { // Fix flot.resize plugin | ||
| test: /node_modules.+jquery\.flot\.resize\.js$/, | ||
| use: { | ||
| loader: StringReplacePlugin.replace({ | ||
| replacements: [{ | ||
| pattern: /\(\s*jQuery\s*,\s*this\s*\)/ig, | ||
| replacement: () => '(jQuery,window)' | ||
| }] | ||
| }) | ||
| } | ||
| }, { // Fix markdown plugin | ||
| test: /node_modules(?:\\|\/)markdown(?:\\|\/).+\.js$/, | ||
| use: { | ||
| loader: StringReplacePlugin.replace({ | ||
| replacements: [{ | ||
| pattern: /if \(typeof define !== 'function'\) \{ var define = require\('amdefine'\)\(module\) \}/ig, | ||
| replacement: () => '' | ||
| }] | ||
| }) | ||
| } | ||
| }] | = | }] |
| +- | ||
| }, | = | }, |
| plugins: [ | +- | |
| new webpack.DefinePlugin({ | ||
| 'process.env': process.env.NODE_ENV | ||
| }), | ||
| new webpack.IgnorePlugin(/codemirror/), | ||
| new CustomLibraryTemplatePlugin() | ||
| ], | ||
| externals: { | = | externals: { |
| 'jquery': 'window.jQuery', | <> | 'jquery': 'jQuery', |
| 'moment': 'window.moment', | 'moment': 'moment', | |
| 'datatables.net': '$.fn.dataTable', | = | 'datatables.net': '$.fn.dataTable', |
| 'spin.js': 'window.Spinner', | <> | 'spin.js': 'Spinner', |
| 'jsdom': 'window.jsdom', | 'jsdom': 'jsdom', | |
| 'd3': 'window.d3', | 'd3': 'd3', | |
| 'eve': 'window.eve', | 'eve': 'eve', | |
| 'velocity': 'window.Velocity', | 'velocity': 'Velocity', | |
| 'hammer': 'window.Hammer', | 'hammer': 'Hammer', | |
| 'raphael': 'window.Raphael', | 'raphael': 'Raphael', | |
| 'jquery-mapael': 'window.Mapael', | 'jquery-mapael': 'Mapael', | |
| 'pace': '"pace-progress"', | 'pace': '"pace-progress"', | |
| 'popper.js': 'Popper', | ||
| 'jquery-validation': 'jQuery', | ||
| = | ||
| // blueimp-file-upload plugin | // blueimp-file-upload plugin | |
| 'canvas-to-blob': 'window.blueimpDataURLtoBlob', | <> | 'canvas-to-blob': 'blueimpDataURLtoBlob', |
| 'blueimp-tmpl': 'window.blueimpTmpl', | 'blueimp-tmpl': 'blueimpTmpl', | |
| 'load-image': 'window.blueimpLoadImage', | 'load-image': 'blueimpLoadImage', | |
| 'load-image-meta': 'null', | = | 'load-image-meta': 'null', |
| 'load-image-scale': 'null', | 'load-image-scale': 'null', | |
| 'load-image-exif': 'null', | 'load-image-exif': 'null', | |
| 'jquery-ui/ui/widget': 'null', | <> | 'jquery-ui/ui/widget': 'null', |
| './jquery.fileupload': 'null', | = | './jquery.fileupload': 'null', |
| './jquery.fileupload-process': 'null', | './jquery.fileupload-process': 'null', | |
| './jquery.fileupload-image': 'null', | './jquery.fileupload-image': 'null', | |
| './jquery.fileupload-video': 'null', | './jquery.fileupload-video': 'null', | |
| './jquery.fileupload-validate': 'null', | './jquery.fileupload-validate': 'null', | |
| // blueimp-gallery plugin | // blueimp-gallery plugin | |
| './blueimp-helper': 'window.jQuery', | <> | './blueimp-helper': 'jQuery', |
| './blueimp-gallery': 'window.blueimpGallery', | './blueimp-gallery': 'blueimpGallery', | |
| './blueimp-gallery-video': 'window.blueimpGallery' | './blueimp-gallery-video': 'blueimpGallery' | |
| } | = | } |
| } | } | |
| // ------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------- | |
| // Sourcemaps | // Sourcemaps | |
| if (conf.sourcemaps) { | if (conf.sourcemaps) { | |
| webpackConfig.devtool = conf.devtool | <> | webpackConfig.devtool = conf.devtool |
| } | = | } |
| // ------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------- | |
| // Minify | // Minify | |
| // Minifies sources by default in production mode | // Minifies sources by default in production mode | |
| if (process.env.NODE_ENV !== 'production' && conf.minify) { | if (process.env.NODE_ENV !== 'production' && conf.minify) { | |
| webpackConfig.plugins.push( | <> | webpackConfig.plugins.push( |
| new UglifyJsPlugin({ | new UglifyJsPlugin({ | |
| uglifyOptions: { | uglifyOptions: { | |
| compress: { | compress: { | |
| warnings: false | warnings: false | |
| } | } | |
| }, | }, | |
| sourceMap: conf.sourcemaps, | sourceMap: conf.sourcemaps, | |
| parallel: true | parallel: true | |
| }) | }) | |
| ) | ) | |
| } | = | } |
| module.exports = webpackConfig | module.exports = webpackConfig |