///////////////////////////////////////////////////////////// // // pgAdmin 4 - PostgreSQL Tools // // Copyright (C) 2013 - 2021, The pgAdmin Development Team // This software is released under the PostgreSQL Licence // ////////////////////////////////////////////////////////////// /* eslint-env node */ // Import file, libraries and plugins const path = require('path'); const webpack = require('webpack'); const fs = require('fs'); const sourceDir = __dirname + '/pgadmin/static'; // webpack.shim.js contains path references for resolve > alias configuration // and other util function used in CommonsChunksPlugin. const webpackShimConfig = require('./webpack.shim'); const PRODUCTION = process.env.NODE_ENV === 'production'; const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const extractStyle = new MiniCssExtractPlugin({ filename: '[name].css', chunkFilename: '[name].css', }); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const CopyPlugin = require('copy-webpack-plugin'); const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); const envType = PRODUCTION ? 'production': 'development'; const devToolVal = PRODUCTION ? false : 'eval'; const analyzerMode = process.env.ANALYZE=='true' ? 'static' : 'disabled'; const outputPath = __dirname + '/pgadmin/static/js/generated'; const pgadminThemesJson = __dirname + '/pgadmin/misc/themes/pgadmin.themes.json'; // Expose libraries in app context so they need not to // require('libname') when used in a module const providePlugin = new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', _: 'underscore', Backbone: 'backbone', Backgrid: 'backgrid', pgAdmin: 'pgadmin', 'moment': 'moment', 'window.moment':'moment', process: 'process/browser', Buffer: ['buffer', 'Buffer'], }); // Helps in debugging each single file, it extracts the module files // from bundle so that they are accessible by search in Chrome's sources panel. // Reference: https://webpack.js.org/plugins/source-map-dev-tool-plugin/#components/sidebar/sidebar.jsx const sourceMapDevToolPlugin = new webpack.SourceMapDevToolPlugin({ filename: '[name].js.map', exclude: /(vendor|codemirror|slickgrid|pgadmin\.js|pgadmin.theme|pgadmin.static|style\.js|popper)/, columns: false, }); // can be enabled using bundle:analyze const bundleAnalyzer = new BundleAnalyzerPlugin({ analyzerMode: analyzerMode, reportFilename: 'analyze_report.html', }); const copyFiles = new CopyPlugin({ patterns: [ pgadminThemesJson, { from: './pgadmin/static/scss/resources/**/*.png', to: 'img/[name].[ext]', }, ], }); const imageMinimizer = new ImageMinimizerPlugin({ test: /\.(jpe?g|png|gif|svg)$/i, minimizerOptions: { // Lossless optimization with custom option // Feel free to experiment with options for better result for you plugins: [ ['mozjpeg', { progressive: true }], ['optipng', { optimizationLevel: 7 }], ['pngquant', {quality: [0.75, .9], speed: 3}], ], }, }); function cssToBeSkiped(curr_path) { /** Skip all templates **/ if(curr_path.indexOf('template') > -1) { return true; } for(let i=0; i< webpackShimConfig.css_bundle_skip.length; i++) { if(path.join(__dirname, webpackShimConfig.css_bundle_skip[i]) === curr_path){ return true; } } return false; } /* Get all the style files recursively and store in array to * give input to webpack. */ function pushModulesStyles(curr_path, pgadminStyles, extn) { /** Skip Directories */ if(cssToBeSkiped(curr_path)) { return; } fs.readdirSync(curr_path).map(function(curr_file) { /** Skip Files */ if(cssToBeSkiped(path.join(curr_path, curr_file))) { return; } let stats = fs.statSync(path.join(curr_path, curr_file)); /* if directory, dig further */ if(stats.isDirectory()) { pushModulesStyles(path.join(curr_path, curr_file), pgadminStyles, extn); } else if(stats.isFile() && (curr_file.endsWith(extn))) { pgadminStyles.push(path.join(curr_path, curr_file)); } }); } let pgadminScssStyles = []; let pgadminCssStyles = []; /* Include what is given in shim config */ for(let i=0; i({ plugins: [ require('autoprefixer')(), ], }), }, }, {loader: 'sass-loader'}, { loader: 'sass-resources-loader', options: { resources: function(_theme_name){ let ret_res = [ './pgadmin/static/scss/resources/pgadmin.resources.scss', ]; if(_theme_name!='standard') { ret_res.unshift('./pgadmin/static/scss/resources/' + _theme_name + '/_theme.variables.scss'); } return ret_res; }(theme_name), }, }, ], }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '', }, }, 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: () =>({ plugins: [ require('autoprefixer')(), ], }), }, }, ], }]; }; var getThemeWebpackConfig = function(theme_name) { return { mode: envType, devtool: devToolVal, stats: { children: false }, // The base directory, an absolute path, for resolving entry points and loaders // from configuration. context: __dirname, // Specify entry points of application entry: { [pgadminThemes[theme_name].cssfile]: pgadminScssStyles, }, // path: The output directory for generated bundles(defined in entry) // Ref: https://webpack.js.org/configuration/output/#output-library output: { libraryTarget: 'amd', path: outputPath, filename: '[name].js', libraryExport: 'default', publicPath: '', }, // Templates files which contains python code needs to load dynamically // Such files specified in externals are loaded at first and defined in // the start of generated bundle within define(['libname'],fn) etc. externals: webpackShimConfig.externals, module: { // References: // Module and Rules: https://webpack.js.org/configuration/module/ // Loaders: https://webpack.js.org/loaders/ // rules: themeCssRules(theme_name), }, optimization: { minimize: true, minimizer: [ new CssMinimizerPlugin({ minimizerOptions: { preset: [ 'default', { discardComments: { removeAll: true }, }, ], } }), ], }, resolve: { alias: webpackShimConfig.resolveAlias, modules: ['node_modules', '.'], extensions: ['.js'], unsafeCache: true, }, // Watch mode Configuration: After initial build, webpack will watch for // changes in files and compiles only files which are changed, // if watch is set to True // Reference: https://webpack.js.org/configuration/watch/#components/sidebar/sidebar.jsx watchOptions: { aggregateTimeout: 300, poll: 1000, ignored: /node_modules/, }, // Define list of Plugins used in Production or development mode // Ref:https://webpack.js.org/concepts/plugins/#components/sidebar/sidebar.jsx plugins: [ extractStyle, sourceMapDevToolPlugin, ] }; }; var pgadminThemesWebpack = []; Object.keys(pgadminThemes).forEach((theme_name)=>{ pgadminThemesWebpack.push(getThemeWebpackConfig(theme_name)); }); module.exports = [{ mode: envType, devtool: devToolVal, stats: { children: false, builtAt: true, chunks: true, timings: true }, // The base directory, an absolute path, for resolving entry points and loaders // from configuration. context: __dirname, // Specify entry points of application entry: { 'app.bundle': sourceDir + '/bundle/app.js', codemirror: sourceDir + '/bundle/codemirror.js', slickgrid: sourceDir + '/bundle/slickgrid.js', sqleditor: './pgadmin/tools/sqleditor/static/js/sqleditor.js', debugger_direct: './pgadmin/tools/debugger/static/js/direct.js', schema_diff: './pgadmin/tools/schema_diff/static/js/schema_diff_hook.js', erd_tool: './pgadmin/tools/erd/static/js/erd_tool_hook.js', psql_tool: './pgadmin/tools/psql/static/js/index.js', file_utils: './pgadmin/misc/file_manager/static/js/utility.js', 'pgadmin.style': pgadminCssStyles, pgadmin: pgadminScssStyles, style: './pgadmin/static/css/style.css', }, // path: The output directory for generated bundles(defined in entry) // Ref: https://webpack.js.org/configuration/output/#output-library output: { libraryTarget: 'amd', path: outputPath, filename: '[name].js', chunkFilename: '[name].chunk.js?id=[chunkhash]', libraryExport: 'default', publicPath: '', }, // Templates files which contains python code needs to load dynamically // Such files specified in externals are loaded at first and defined in // the start of generated bundle within define(['libname'],fn) etc. externals: webpackShimConfig.externals, module: { // References: // Module and Rules: https://webpack.js.org/configuration/module/ // Loaders: https://webpack.js.org/loaders/ // // imports-loader: it adds dependent modules(use:imports-loader?module1) // at the beginning of module it is dependency of like: // var jQuery = require('jquery'); var browser = require('pgadmin.browser') // It solves number of problems // Ref: http:/github.com/webpack-contrib/imports-loader/ rules: [{ test: /\.fonticon\.js/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { url: false, }, }, 'webfonts-loader', ], },{ test: /\.jsx?$/, exclude: [/node_modules/, /vendor/], use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', {'modules': 'commonjs', 'useBuiltIns': 'usage', 'corejs': 3}], '@babel/preset-react', '@babel/preset-typescript'], plugins: ['@babel/plugin-proposal-class-properties', '@babel/proposal-object-rest-spread'], }, }, }, { test: /\.tsx?$|\.ts?$/, use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', {'modules': 'commonjs', 'useBuiltIns': 'usage', 'corejs': 3}], '@babel/preset-react', '@babel/preset-typescript'], plugins: ['@babel/plugin-proposal-class-properties', '@babel/proposal-object-rest-spread'], }, }, }, { test: /external_table.*\.js/, use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', {'modules': 'commonjs', 'useBuiltIns': 'usage', 'corejs': 3}]], }, }, }, { // Transforms the code in a way that it works in the webpack environment. // It uses imports-loader internally to load dependency. Its // configuration is specified in webpack.shim.js // Ref: https://www.npmjs.com/package/shim-loader test: /\.js/, exclude: [/external_table/], loader: 'shim-loader', options: webpackShimConfig, include: path.join(__dirname, '/pgadmin/browser'), }, { // imports-loader: it adds dependent modules(use:imports-loader?module1) // at the beginning of module it is dependency of like: // var jQuery = require('jquery'); var browser = require('pgadmin.browser') // It solves number of problems // Ref: http:/github.com/webpack-contrib/imports-loader/ test: require.resolve('./pgadmin/tools/datagrid/static/js/datagrid'), use: { loader: 'imports-loader', options: { type: 'commonjs', imports: [ 'pure|pgadmin.dashboard', 'pure|pgadmin.browser.quick_search', 'pure|pgadmin.tools.user_management', 'pure|pgadmin.browser.object_statistics', 'pure|pgadmin.browser.dependencies', 'pure|pgadmin.browser.dependents', 'pure|pgadmin.browser.object_sql', 'pure|pgadmin.browser.bgprocess', 'pure|pgadmin.node.server_group', 'pure|pgadmin.node.server', 'pure|pgadmin.node.database', 'pure|pgadmin.node.role', 'pure|pgadmin.node.cast', 'pure|pgadmin.node.publication', 'pure|pgadmin.node.subscription', 'pure|pgadmin.node.tablespace', 'pure|pgadmin.node.resource_group', 'pure|pgadmin.node.event_trigger', 'pure|pgadmin.node.extension', 'pure|pgadmin.node.language', 'pure|pgadmin.node.foreign_data_wrapper', 'pure|pgadmin.node.foreign_server', 'pure|pgadmin.node.user_mapping', 'pure|pgadmin.node.schema', 'pure|pgadmin.node.catalog', 'pure|pgadmin.node.catalog_object', 'pure|pgadmin.node.collation', 'pure|pgadmin.node.domain', 'pure|pgadmin.node.domain_constraints', 'pure|pgadmin.node.foreign_table', 'pure|pgadmin.node.fts_configuration', 'pure|pgadmin.node.fts_dictionary', 'pure|pgadmin.node.fts_parser', 'pure|pgadmin.node.fts_template', 'pure|pgadmin.node.function', 'pure|pgadmin.node.procedure', 'pure|pgadmin.node.edbfunc', 'pure|pgadmin.node.edbproc', 'pure|pgadmin.node.edbvar', 'pure|pgadmin.node.trigger_function', 'pure|pgadmin.node.package', 'pure|pgadmin.node.sequence', 'pure|pgadmin.node.synonym', 'pure|pgadmin.node.type', 'pure|pgadmin.node.rule', 'pure|pgadmin.node.index', 'pure|pgadmin.node.row_security_policy', 'pure|pgadmin.node.trigger', 'pure|pgadmin.node.catalog_object_column', 'pure|pgadmin.node.view', 'pure|pgadmin.node.mview', 'pure|pgadmin.node.table', 'pure|pgadmin.node.partition', 'pure|pgadmin.node.compound_trigger', 'pure|pgadmin.node.aggregate', 'pure|pgadmin.node.operator', ], }, }, },{ test: require.resolve('./node_modules/acitree/js/jquery.aciTree.min'), use: { loader: 'imports-loader', options: { wrapper: 'window', }, }, }, { test: require.resolve('./node_modules/acitree/js/jquery.aciPlugin.min'), use: { loader: 'imports-loader', options: { wrapper: 'window', }, }, }, { test: require.resolve('./pgadmin/static/bundle/browser'), use: { loader: 'imports-loader', options: { type: 'commonjs', imports: [ 'pure|pgadmin.about', 'pure|pgadmin.preferences', 'pure|pgadmin.file_manager', 'pure|pgadmin.settings', 'pure|pgadmin.tools.backup', 'pure|pgadmin.tools.restore', 'pure|pgadmin.tools.grant_wizard', 'pure|pgadmin.tools.maintenance', 'pure|pgadmin.tools.import_export', 'pure|pgadmin.tools.debugger.controller', 'pure|pgadmin.tools.debugger.direct', 'pure|pgadmin.node.pga_job', 'pure|pgadmin.tools.schema_diff', 'pure|pgadmin.tools.storage_manager', 'pure|pgadmin.tools.search_objects', 'pure|pgadmin.tools.erd_module', 'pure|pgadmin.tools.psql_module', ], }, }, }].concat(themeCssRules('standard')), // Prevent module from parsing through webpack, helps in reducing build time noParse: [/moment.js/], }, resolve: { alias: webpackShimConfig.resolveAlias, modules: ['node_modules', '.'], extensions: ['.js', '.jsx', '.ts', '.tsx'], unsafeCache: true, }, // Watch mode Configuration: After initial build, webpack will watch for // changes in files and compiles only files which are changed, // if watch is set to True // Reference: https://webpack.js.org/configuration/watch/#components/sidebar/sidebar.jsx watchOptions: { aggregateTimeout: 300, poll: 1000, ignored: /node_modules/, }, optimization: { minimizer: [ new TerserPlugin({ parallel: true, extractComments: true, terserOptions: { compress: true, }, }), ], splitChunks: { cacheGroups: { vendor_main: { name: 'vendor_main', filename: 'vendor.main.js', chunks: 'all', reuseExistingChunk: true, priority: 7, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.matchModules(module, ['wcdocker', 'backbone', 'jquery', 'bootstrap', 'popper']); }, }, vendor_others: { name: 'vendor_others', filename: 'vendor.others.js', chunks: 'all', reuseExistingChunk: true, priority: 6, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.isExternal(module); }, }, secondary: { name: 'pgadmin_commons', filename: 'pgadmin_commons.js', chunks: 'all', priority: 5, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.isPgAdminLib(module); }, }, browser_nodes: { name: 'browser_nodes', filename: 'browser_nodes.js', chunks: 'all', priority: 4, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.isBrowserNode(module); }, }, }, }, }, // Define list of Plugins used in Production or development mode // Ref:https://webpack.js.org/concepts/plugins/#components/sidebar/sidebar.jsx plugins: PRODUCTION ? [ extractStyle, providePlugin, sourceMapDevToolPlugin, bundleAnalyzer, copyFiles, imageMinimizer, ]: [ extractStyle, providePlugin, sourceMapDevToolPlugin, copyFiles, ], }].concat(pgadminThemesWebpack);