* Improved development server Allow running the D-Installer module from a local machine, forward the connection to the target Cockpit instance * Always use HTTPS, even for a local Cockpit * Allow external connections * Login from the development server automatically * Handle connection errors * Show target info in the sidebar * Do not link to localhost from outside * Added unit tests
202 lines
6.4 KiB
JavaScript
202 lines
6.4 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const Copy = require("copy-webpack-plugin");
|
|
const Extract = require("mini-css-extract-plugin");
|
|
const TerserJSPlugin = require('terser-webpack-plugin');
|
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
|
const CompressionPlugin = require("compression-webpack-plugin");
|
|
const ESLintPlugin = require('eslint-webpack-plugin');
|
|
const CockpitPoPlugin = require("./src/lib/cockpit-po-plugin");
|
|
const CockpitRsyncPlugin = require("./src/lib/cockpit-rsync-plugin");
|
|
const StylelintPlugin = require('stylelint-webpack-plugin');
|
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
|
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
|
const webpack = require('webpack');
|
|
|
|
/* A standard nodejs and webpack pattern */
|
|
const production = process.env.NODE_ENV === 'production';
|
|
const development = !production;
|
|
|
|
/* development options for faster iteration */
|
|
const eslint = process.env.ESLINT !== '0';
|
|
|
|
/* Default to disable csslint for faster production builds */
|
|
const stylelint = process.env.STYLELINT ? (process.env.STYLELINT !== '0') : development;
|
|
|
|
// Cockpit target managed by the development server,
|
|
// by default connect to a locally running Cockpit
|
|
let cockpitTarget = process.env.COCKPIT_TARGET || "localhost";
|
|
// add the default port if not specified
|
|
if (cockpitTarget.indexOf(":") === -1) cockpitTarget += ":9090";
|
|
cockpitTarget = "https://" + cockpitTarget;
|
|
|
|
// Obtain package name from package.json
|
|
const packageJson = JSON.parse(fs.readFileSync('package.json'));
|
|
|
|
// Non-JS files which are copied verbatim to dist/
|
|
const copy_files = [
|
|
"./src/index.html",
|
|
"./src/manifest.json",
|
|
// TODO: consider using something more complete like https://github.com/jantimon/favicons-webpack-plugin
|
|
"./src/assets/favicon.svg",
|
|
];
|
|
|
|
const plugins = [
|
|
new Copy({ patterns: copy_files }),
|
|
new Extract({ filename: "[name].css" }),
|
|
new CockpitPoPlugin(),
|
|
new CockpitRsyncPlugin({ dest: packageJson.name }),
|
|
development && new ReactRefreshWebpackPlugin({ overlay: false }),
|
|
// replace the "process.env.WEBPACK_SERVE" text in the source code by
|
|
// the current value of the environment variable, that variable is set to
|
|
// "true" when running the development server ("npm run server")
|
|
// https://webpack.js.org/plugins/environment-plugin/
|
|
new webpack.EnvironmentPlugin({ WEBPACK_SERVE: null }),
|
|
// similarly for a non-environment value
|
|
// https://webpack.js.org/plugins/define-plugin/
|
|
// but because ESlint runs *before* the DefinePlugin we need to
|
|
// add it as a global variable in .eslintrc.json config file
|
|
new webpack.DefinePlugin({ COCKPIT_TARGET_URL: JSON.stringify(cockpitTarget) }),
|
|
].filter(Boolean);
|
|
|
|
if (eslint) {
|
|
plugins.push(new ESLintPlugin({ extensions: ["js", "jsx"], failOnWarning: true, }));
|
|
}
|
|
|
|
if (stylelint) {
|
|
plugins.push(new StylelintPlugin({
|
|
context: "src/",
|
|
}));
|
|
}
|
|
|
|
/* Only minimize when in production mode */
|
|
if (production) {
|
|
plugins.unshift(new CompressionPlugin({
|
|
test: /\.(js|html|css)$/,
|
|
deleteOriginalAssets: true
|
|
}));
|
|
}
|
|
|
|
module.exports = {
|
|
mode: production ? 'production' : 'development',
|
|
resolve: {
|
|
modules: ["node_modules", path.resolve(__dirname, 'src/lib')],
|
|
plugins: [new TsconfigPathsPlugin({ extensions: [".js", ".jsx", ".json"] })],
|
|
extensions: ['', '.js', '.json', '.jsx']
|
|
},
|
|
resolveLoader: {
|
|
modules: ["node_modules", path.resolve(__dirname, 'src/lib')],
|
|
},
|
|
watchOptions: {
|
|
ignored: /node_modules/,
|
|
},
|
|
entry: {
|
|
reactRefreshSetup: '@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js',
|
|
index: ["./src/index.js"],
|
|
},
|
|
// cockpit.js gets included via <script>, everything else should be bundled
|
|
externals: { cockpit: "cockpit" },
|
|
devServer: {
|
|
hot: true,
|
|
// forward all cockpit connections to a real Cockpit instance
|
|
proxy: {
|
|
"/cockpit": {
|
|
target: cockpitTarget,
|
|
// redirect also the websocket connections
|
|
ws: true,
|
|
// ignore SSL problems (self-signed certificate)
|
|
secure: false,
|
|
},
|
|
},
|
|
// use https so Cockpit uses wss:// when connecting to the backend
|
|
server: "https",
|
|
// hot replacement does not support wss:// transport when running over https://,
|
|
// as a workaround use sockjs (which uses standard https:// protocol)
|
|
webSocketServer: "sockjs",
|
|
},
|
|
devtool: "source-map",
|
|
stats: "errors-warnings",
|
|
// always regenerate dist/, so make rules work
|
|
output: { clean: true, compareBeforeEmit: false },
|
|
|
|
optimization: {
|
|
minimize: production,
|
|
minimizer: [
|
|
new TerserJSPlugin({
|
|
extractComments: {
|
|
condition: true,
|
|
filename: `[file].LICENSE.txt?query=[query]&filebase=[base]`,
|
|
banner(licenseFile) {
|
|
return `License information can be found in ${licenseFile}`;
|
|
},
|
|
},
|
|
}),
|
|
new CssMinimizerPlugin()
|
|
],
|
|
},
|
|
|
|
module: {
|
|
rules: [
|
|
{
|
|
test: /\.(js|jsx)$/,
|
|
exclude: /node_modules/,
|
|
use: [
|
|
{
|
|
loader: "babel-loader",
|
|
options: {
|
|
plugins: [development && require.resolve('react-refresh/babel')].filter(Boolean),
|
|
},
|
|
}
|
|
]
|
|
},
|
|
{
|
|
test: /\.s?css$/,
|
|
use: [
|
|
Extract.loader,
|
|
{
|
|
loader: 'css-loader',
|
|
options: {
|
|
sourceMap: true,
|
|
url: {
|
|
// Only follow D-Installer fonts links to be processed by the next rule and place
|
|
// them in dist/fonts
|
|
filter: (url) => url.includes("./fonts/")
|
|
}
|
|
}
|
|
},
|
|
{
|
|
loader: 'sass-loader',
|
|
options: {
|
|
sourceMap: development,
|
|
sassOptions: {
|
|
includePaths: ["node_modules"],
|
|
outputStyle: production ? 'compressed' : undefined,
|
|
},
|
|
},
|
|
},
|
|
]
|
|
},
|
|
{
|
|
test: /\.(eot|ttf|woff|woff2)$/,
|
|
type: 'asset/resource',
|
|
generator: {
|
|
filename: 'fonts/[name][ext]',
|
|
}
|
|
},
|
|
// Load SVG files
|
|
{
|
|
test: /\.svg$/i,
|
|
type: 'asset',
|
|
resourceQuery: { not: [/component/] } // exclude file import includes ""?component"
|
|
},
|
|
{
|
|
test: /\.svg$/i,
|
|
issuer: /\.jsx?$/,
|
|
resourceQuery: /component/, // *.svg?component
|
|
use: ['@svgr/webpack']
|
|
}
|
|
]
|
|
},
|
|
plugins: plugins
|
|
};
|