const path = require('path'); const DynamicRemotePlugin = require('@openshift/dynamic-plugin-sdk-webpack').DynamicRemotePlugin; const CSSMinimizerPlugin = require('css-minimizer-webpack-plugin'); const webpack = require('webpack'); const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const pluginMetadata = require('./plugin-metadata.json'); // HCC development proxy const proxy = require('@redhat-cloud-services/frontend-components-config-utilities/proxy'); const fs = require('fs'); const BG_IMAGES_DIRNAME = 'assets'; const isProd = process.env.NODE_ENV === 'production'; const pathTo = (relativePath) => path.resolve(__dirname, relativePath); const publicPath = process.env.BETA === 'true' ? '/beta/apps/quay/' : '/apps/quay/'; /** * Shared modules consumed and/or provided by this plugin. * * A host application typically provides some modules to its plugins. If an application * provided module is configured as an eager singleton, we suggest using `import: false` * to avoid bundling a fallback version of the module when building your plugin. * * Plugins may provide additional shared modules that can be consumed by other plugins. * * @see https://webpack.js.org/plugins/module-federation-plugin/#sharing-hints */ const pluginSharedModules = { '@openshift/dynamic-plugin-sdk': {singleton: true, import: false}, '@patternfly/react-core': {}, '@patternfly/react-table': {}, react: {singleton: true, import: false}, 'react-dom': {singleton: true, import: false}, 'react-router-dom': {singleton: true, import: false}, '@scalprum/react-core': {singleton: true, import: false}, }; const plugins = [ new webpack.EnvironmentPlugin({ NODE_ENV: 'development', }), // required for SDK code new webpack.ProvidePlugin({ process: 'process/browser', }), new DynamicRemotePlugin({ pluginMetadata, extensions: [], sharedModules: pluginSharedModules, entryScriptFilename: isProd ? 'plugin-entry.[fullhash].min.js' : 'plugin-entry.js', }), ]; module.exports = { mode: isProd ? 'production' : 'development', entry: {}, // Plugin container entry is generated by DynamicRemotePlugin output: { path: pathTo('dist'), publicPath: publicPath, chunkFilename: isProd ? 'chunks/[id].[chunkhash].min.js' : 'chunks/[id].js', assetModuleFilename: isProd ? 'assets/[contenthash][ext]' : 'assets/[name][ext]', }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], fallback: { 'process/browser': require.resolve('process/browser'), }, plugins: [ new TsconfigPathsPlugin({ configFile: path.resolve(__dirname, './tsconfig.json'), }), ], symlinks: false, cacheWithContext: false, }, module: { rules: [ { test: /\.(jsx?|tsx?)$/, exclude: /\/node_modules\//, use: [ { loader: 'ts-loader', options: { transpileOnly: true, experimentalWatchApi: true, }, }, ], }, { test: /\.(svg|ttf|eot|woff|woff2)$/, // only process modules with this loader // if they live under a 'fonts' or 'pficon' directory include: [ path.resolve(__dirname, 'node_modules/patternfly/dist/fonts'), path.resolve( __dirname, 'node_modules/@patternfly/react-core/dist/styles/assets/fonts', ), path.resolve( __dirname, 'node_modules/@patternfly/react-core/dist/styles/assets/pficon', ), path.resolve( __dirname, 'node_modules/@patternfly/patternfly/assets/fonts', ), path.resolve( __dirname, 'node_modules/@patternfly/patternfly/assets/pficon', ), ], generator: { filename: 'assets/[name].[ext]', }, }, { test: /\.svg$/, include: (input) => input.indexOf('background-filter.svg') > 1, type: 'asset', }, { test: /\.svg$/, // only process SVG modules with this loader if they live under a 'bgimages' directory // this is primarily useful when applying a CSS background using an SVG include: (input) => input.indexOf(BG_IMAGES_DIRNAME) > -1, type: 'asset/inline', }, { test: /\.svg$/i, // only process SVG modules with this loader when they don't live under a 'bgimages', // 'fonts', or 'pficon' directory, those are handled with other loaders include: (input) => input.indexOf(BG_IMAGES_DIRNAME) === -1 && input.indexOf('fonts') === -1 && input.indexOf('background-filter') === -1 && input.indexOf('pficon') === -1, type: 'asset/resource', }, { test: /\.(jpg|jpeg|png|gif)$/i, include: [ path.resolve(__dirname, 'node_modules/patternfly'), path.resolve( __dirname, 'node_modules/@patternfly/patternfly/assets/images', ), path.resolve( __dirname, 'node_modules/@patternfly/react-styles/css/assets/images', ), path.resolve( __dirname, 'node_modules/@patternfly/react-core/dist/styles/assets/images', ), path.resolve( __dirname, 'node_modules/@patternfly/react-core/node_modules/@patternfly/react-styles/css/assets/images', ), path.resolve( __dirname, 'node_modules/@patternfly/react-table/node_modules/@patternfly/react-styles/css/assets/images', ), path.resolve( __dirname, 'node_modules/@patternfly/react-inline-edit-extension/node_modules/@patternfly/react-styles/css/assets/images', ), ], type: 'asset', }, { test: /\.s[ac]ss$/i, use: [ // Creates `style` nodes from JS strings 'style-loader', // Translates CSS into CommonJS 'css-loader', // Compiles Sass to CSS 'sass-loader', ], }, { test: /\.(css)$/, use: ['style-loader', 'css-loader'], }, ], }, plugins, devtool: isProd ? 'source-map' : 'cheap-source-map', optimization: { minimize: isProd, minimizer: [ '...', // The '...' string represents the webpack default TerserPlugin instance new CSSMinimizerPlugin(), ], }, devServer: { static: pathTo('dist'), port: 1337, https: true, host: '0.0.0.0', allowedHosts: 'all', // https://github.com/bripkens/connect-history-api-fallback historyApiFallback: { // We should really implement the same logic as cloud-services-config // and only redirect (/beta)?/bundle/app-name to /index.html rewrites: [ {from: /^\/api/, to: '/404.html'}, {from: /^(\/beta)?\/config/, to: '/404.html'}, ], verbose: true, disableDotRule: true, }, devMiddleware: { writeToDisk: true, }, client: { overlay: false, }, ...proxy({ localChrome: '/Users/syed/work/redhat/consoledot-integration/insights-chrome/build/', useProxy: true, env: 'stage-stable', port: 1337, appUrl: ['/settings/quay', '/beta/settings/quay'], publicPath: publicPath, proxyVerbose: true, onBeforeSetupMiddleware: ({chromePath}) => { if (chromePath) { const outputPath = pathTo('dist'); const template = fs.readFileSync(`${chromePath}/index.html`, { encoding: 'utf-8', }); if (!fs.existsSync(outputPath)) { fs.mkdirSync(outputPath); } fs.writeFileSync(`${outputPath}/index.html`, template); } }, useDevBuild: true, routes: { '/config/chrome': { host: 'http://localhost:8003', }, '/beta/config/chrome': { host: 'http://localhost:8003', }, '/api/chrome-service/v1/static/stable/stage/navigation': { host: 'http://localhost:8003', }, }, }), }, };