///////////////////////////////////////////////////////////// // // 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 OptimizeCssAssetsPlugin = require('optimize-css-assets-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', allChunks: true, }); const WebpackRequireFromPlugin = require('webpack-require-from'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const CopyPlugin = require('copy-webpack-plugin'); const IconfontWebpackPlugin = require('iconfont-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', }); // Optimize CSS Assets by removing comments while bundling const optimizeAssetsPlugin = new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano'), cssProcessorOptions: { discardComments: { removeAll: true, }, }, canPrint: true, }); // 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, }); // Supress errors while compiling as the getChunkURL method will be available // on runtime. window.getChunkURL is defined in base.html const webpackRequireFrom = new WebpackRequireFromPlugin({ methodName: 'getChunkURL', supressErrors: true, }); // can be enabled using bundle:analyze const bundleAnalyzer = new BundleAnalyzerPlugin({ analyzerMode: analyzerMode, reportFilename: 'analyze_report.html', }); const copyFiles = new CopyPlugin([ pgadminThemesJson, { from: './pgadmin/static/scss/resources/**/*.png', to: outputPath + '/img', flatten: true, }, ]); 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 [ require('autoprefixer')(), new IconfontWebpackPlugin(loader), ], }, }, {loader: 'sass-loader'}, { loader: 'sass-resources-loader', options: { resources: function(_theme_name){ let ret_res = [ './pgadmin/static/scss/resources/' + _theme_name + '/_theme.variables.scss', './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: [ MiniCssExtractPlugin.loader, 'css-loader', { loader: 'postcss-loader', options: { plugins: (loader) => [ require('autoprefixer')(), new IconfontWebpackPlugin(loader), ], }, }, ], }]; }; 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', }, // 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), }, 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: PRODUCTION ? [ extractStyle, optimizeAssetsPlugin, sourceMapDevToolPlugin, ]: [ extractStyle, sourceMapDevToolPlugin, ], }; }; var pgadminThemesWebpack = []; Object.keys(pgadminThemes).map((theme_name)=>{ pgadminThemesWebpack.push(getThemeWebpackConfig(theme_name)); }); module.exports = [{ 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: { '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', 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', libraryExport: 'default', }, // 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: /\.jsx?$/, exclude: [/node_modules/, /vendor/], use: { loader: 'babel-loader', options: { presets: [['@babel/preset-env', {'modules': 'commonjs', 'useBuiltIns': 'usage', 'corejs': 3}], '@babel/preset-react'], plugins: ['@babel/plugin-proposal-class-properties'], }, }, }, { 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', query: 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?' + 'pgadmin.dashboard' + ',pgadmin.browser.quick_search' + ',pgadmin.tools.user_management' + ',pgadmin.browser.object_statistics' + ',pgadmin.browser.dependencies' + ',pgadmin.browser.dependents' + ',pgadmin.browser.object_sql' + ',pgadmin.browser.bgprocess' + ',pgadmin.node.server_group' + ',pgadmin.node.server' + ',pgadmin.node.database' + ',pgadmin.node.role' + ',pgadmin.node.cast' + ',pgadmin.node.publication' + ',pgadmin.node.subscription' + ',pgadmin.node.tablespace' + ',pgadmin.node.resource_group' + ',pgadmin.node.event_trigger' + ',pgadmin.node.extension' + ',pgadmin.node.language' + ',pgadmin.node.foreign_data_wrapper' + ',pgadmin.node.foreign_server' + ',pgadmin.node.user_mapping' + ',pgadmin.node.schema' + ',pgadmin.node.catalog' + ',pgadmin.node.catalog_object' + ',pgadmin.node.collation' + ',pgadmin.node.domain' + ',pgadmin.node.domain_constraints' + ',pgadmin.node.foreign_table' + ',pgadmin.node.fts_configuration' + ',pgadmin.node.fts_dictionary' + ',pgadmin.node.fts_parser' + ',pgadmin.node.fts_template' + ',pgadmin.node.function' + ',pgadmin.node.procedure' + ',pgadmin.node.edbfunc' + ',pgadmin.node.edbproc' + ',pgadmin.node.edbvar' + ',pgadmin.node.edbvar' + ',pgadmin.node.trigger_function' + ',pgadmin.node.package' + ',pgadmin.node.sequence' + ',pgadmin.node.synonym' + ',pgadmin.node.type' + ',pgadmin.node.rule' + ',pgadmin.node.index' + ',pgadmin.node.row_security_policy' + ',pgadmin.node.trigger' + ',pgadmin.node.catalog_object_column' + ',pgadmin.node.view' + ',pgadmin.node.mview' + ',pgadmin.node.table' + ',pgadmin.node.partition' + ',pgadmin.node.compound_trigger', }, }, { test: require.resolve('./node_modules/acitree/js/jquery.aciTree.min'), use: { loader: 'imports-loader?this=>window', }, }, { test: require.resolve('./node_modules/acitree/js/jquery.aciPlugin.min'), use: { loader: 'imports-loader?this=>window', }, }, { test: require.resolve('./pgadmin/static/bundle/browser'), use: { loader: 'imports-loader?' + 'pgadmin.about' + ',pgadmin.preferences' + ',pgadmin.file_manager' + ',pgadmin.settings' + ',pgadmin.tools.backup' + ',pgadmin.tools.restore' + ',pgadmin.tools.grant_wizard' + ',pgadmin.tools.maintenance' + ',pgadmin.tools.import_export' + ',pgadmin.tools.debugger.controller' + ',pgadmin.tools.debugger.direct' + ',pgadmin.node.pga_job' + ',pgadmin.tools.schema_diff' + ',pgadmin.tools.storage_manager' + ',pgadmin.tools.search_objects' + ',pgadmin.tools.erd_module', }, }, { test: require.resolve('snapsvg'), use: { loader: 'imports-loader?this=>window,fix=>module.exports=0', }, }].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'], 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, cache: true, terserOptions: { compress: true, extractComments: true, output: { comments: false, }, }, }), ], splitChunks: { cacheGroups: { slickgrid: { name: 'slickgrid', filename: 'slickgrid.js', chunks: 'all', reuseExistingChunk: true, priority: 9, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.matchModules(module, 'slickgrid'); }, }, codemirror: { name: 'codemirror', filename: 'codemirror.js', chunks: 'all', reuseExistingChunk: true, priority: 8, minChunks: 2, enforce: true, test(module) { return webpackShimConfig.matchModules(module, 'codemirror'); }, }, 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, optimizeAssetsPlugin, sourceMapDevToolPlugin, webpackRequireFrom, bundleAnalyzer, copyFiles, ]: [ extractStyle, providePlugin, sourceMapDevToolPlugin, webpackRequireFrom, copyFiles, ], }].concat(pgadminThemesWebpack);