Files
redash/webpack.config.js
Vladislav Denisov 24b70fed9e Update frontend stack (#7651)
* Snapshot: 24.07.0-dev

* Snapshot: 24.08.0-dev

* Snapshot: 24.09.0-dev

* Snapshot: 24.10.0-dev

* Snapshot: 24.11.0-dev

* Snapshot: 24.12.0-dev

* Snapshot: 25.01.0-dev

* Snapshot: 25.02.0-dev

* Snapshot: 25.03.0-dev

* Snapshot: 25.04.0-dev

* Upgrade Node.js version to 24 in Dockerfile and .nvmrc; update package.json engine constraints

* Update major dependencies

* Switch from yarn to pnpm

* Switch from yarn to pnpm: ci

* Update Python version to 3.13 in CI workflow

* Refactor Netlify build command to remove pnpm installation step

* Update ESLint configuration for improved compatibility and disable specific rules

* Restyled by prettier

* Add typeRoots and types to tsconfig for improved type definitions

* Update Dockerfile.cypress to use Node 24 and streamline installation steps

* Fixed tests

* Restyled by prettier

* Update Jest snapshot comments to point to the official documentation URL

* viz-lib: refactor test setup and update snapshots for consistency

* Add babel-jest as a dev dependency for improved testing support

* Add virtual prop to visualization type selector for improved functionality

* Remove CJS/ESM compatibility shim for color-rgba

* Restyled by prettier

* Enable ESLintPlugin conditionally based on production environment

* Import d3 library in d3box.ts

* Fix pip install command in CI workflow to use python -m

* Replace d3 import with global declaration for compatibility with d3 v3

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Restyled.io <commits@restyled.io>
2026-03-17 20:54:25 +00:00

319 lines
8.9 KiB
JavaScript

/* eslint-disable */
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WebpackBuildNotifierPlugin = require("webpack-build-notifier");
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const LessPluginAutoPrefix = require("less-plugin-autoprefix");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
const ESLintPlugin = require("eslint-webpack-plugin");
const path = require("path");
function optionalRequire(module, defaultReturn = undefined) {
try {
require.resolve(module);
} catch (e) {
if (e && e.code === "MODULE_NOT_FOUND") {
// Module was not found, return default value if any
return defaultReturn;
}
throw e;
}
return require(module);
}
// Load optionally configuration object (see scripts/README)
const CONFIG = optionalRequire("./scripts/config", {});
const isProduction = process.env.NODE_ENV === "production";
const isDevelopment = !isProduction;
const isHotReloadingEnabled =
isDevelopment && process.env.HOT_RELOAD === "true";
const redashBackend = process.env.REDASH_BACKEND || "http://localhost:5001";
const baseHref = CONFIG.baseHref || "/";
const staticPath = CONFIG.staticPath || "/static/";
const htmlTitle = CONFIG.title || "Redash";
const basePath = path.join(__dirname, "client");
const appPath = path.join(__dirname, "client", "app");
const extensionsRelativePath =
process.env.EXTENSIONS_DIRECTORY || path.join("client", "app", "extensions");
const extensionPath = path.join(__dirname, extensionsRelativePath);
// Function to apply configuration overrides (see scripts/README)
function maybeApplyOverrides(config) {
const overridesLocation =
process.env.REDASH_WEBPACK_OVERRIDES || "./scripts/webpack/overrides";
const applyOverrides = optionalRequire(overridesLocation);
if (!applyOverrides) {
return config;
}
console.info("Custom overrides found. Applying them...");
const newConfig = applyOverrides(config);
console.info("Custom overrides applied successfully.");
return newConfig;
}
const config = {
mode: isProduction ? "production" : "development",
entry: {
app: [
"./client/app/index.js",
"./client/app/assets/less/main.less",
"./client/app/assets/less/ant.less"
],
server: ["./client/app/assets/less/server.less"]
},
output: {
path: path.join(basePath, "./dist"),
filename: isProduction ? "[name].[chunkhash].js" : "[name].js",
publicPath: staticPath
},
node: {
},
resolve: {
symlinks: true,
extensions: [".js", ".jsx", ".ts", ".tsx"],
alias: {
"@": appPath,
extensions: extensionPath
},
fallback: {
fs: false,
url: require.resolve("url/"),
stream: require.resolve("stream-browserify"),
assert: require.resolve("assert/"),
util: require.resolve("util/"),
process: require.resolve("process/browser"),
}
},
plugins: [
new WebpackBuildNotifierPlugin({ title: "Redash" }),
// bundle only default `moment` locale (`en`)
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
new HtmlWebpackPlugin({
template: "./client/app/index.html",
filename: "index.html",
excludeChunks: ["server"],
release: process.env.BUILD_VERSION || "dev",
staticPath,
baseHref,
title: htmlTitle
}),
new HtmlWebpackPlugin({
template: "./client/app/multi_org.html",
filename: "multi_org.html",
excludeChunks: ["server"]
}),
isProduction &&
new MiniCssExtractPlugin({
filename: "[name].[chunkhash].css"
}),
new WebpackManifestPlugin({
fileName: "asset-manifest.json",
publicPath: ""
}),
new CopyWebpackPlugin({
patterns: [
{ from: "client/app/assets/robots.txt" },
{ from: "client/app/unsupported.html" },
{ from: "client/app/unsupportedRedirect.js" },
{ from: "client/app/assets/css/*.css", to: "styles/[name][ext]" },
{ from: "client/app/assets/fonts", to: "fonts/" }
],
}),
isHotReloadingEnabled && new ReactRefreshWebpackPlugin({ overlay: false }),
!isProduction &&
new ESLintPlugin({
extensions: ["js", "jsx", "ts", "tsx"],
context: path.resolve(__dirname, "client"),
eslintPath: require.resolve("eslint"),
failOnError: false,
}),
new webpack.ProvidePlugin({
// Make a global `process` variable that points to the `process` package,
// because the `util` package expects there to be a global variable named `process`.
// Thanks to https://stackoverflow.com/a/65018686/14239942
process: 'process/browser'
})
].filter(Boolean),
optimization: {
splitChunks: {
chunks: chunk => {
return chunk.name != "server";
}
}
},
module: {
rules: [
{
test: /\.js$/,
enforce: "pre",
use: ["source-map-loader"],
resolve: {
fullySpecified: false
},
exclude: [
/node_modules\/@plotly\/mapbox-gl/,
],
},
{
test: /\.(t|j)sx?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve("babel-loader"),
options: {
plugins: [
isHotReloadingEnabled && require.resolve("react-refresh/babel")
].filter(Boolean)
}
}
]
},
{
test: /\.html$/,
exclude: [/node_modules/, /index\.html/, /multi_org\.html/],
type: "asset/source"
},
{
test: /\.css$/,
use: [
{
loader: isProduction ? MiniCssExtractPlugin.loader : "style-loader"
},
{
loader: "css-loader"
}
]
},
{
test: /\.less$/,
use: [
{
loader: isProduction ? MiniCssExtractPlugin.loader : "style-loader"
},
{
loader: "css-loader"
},
{
loader: "less-loader",
options: {
sourceMap: false,
lessOptions: {
plugins: [
new LessPluginAutoPrefix({ browsers: ["last 3 versions"] })
],
javascriptEnabled: true
}
}
}
]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
type: "asset/resource",
generator: {
filename: (pathData) => {
const filePath = pathData.filename || "";
// Strip source prefix so assets/images/db-logos/foo.png → db-logos/foo.png
const m = filePath.match(/assets\/images\/(.*)/);
if (m) return `images/${m[1]}`;
// For images from node_modules or elsewhere, flatten to avoid deep paths
const parts = filePath.split("/");
return `images/${parts[parts.length - 1]}`;
}
}
},
{
test: /\.geo\.json$/,
type: "asset/resource",
generator: {
filename: "data/[hash:7].[name][ext]"
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10000
}
},
generator: {
filename: "fonts/[name].[hash:7][ext]"
}
}
]
},
devtool: isProduction ? "source-map" : "eval-cheap-module-source-map",
stats: {
children: false,
modules: false,
chunkModules: false
},
watchOptions: {
ignored: /\.sw.$/
},
devServer: {
devMiddleware: {
index: "/static/index.html",
publicPath: staticPath,
stats: {
modules: false,
chunkModules: false
},
},
historyApiFallback: {
index: "/static/index.html",
rewrites: [{ from: /./, to: "/static/index.html" }]
},
proxy: [
{
context: [
"/login",
"/logout",
"/invite",
"/setup",
"/status.json",
"/api",
"/oauth"
],
target: redashBackend + "/",
changeOrigin: false,
secure: false
},
{
context: path => {
// CSS/JS for server-rendered pages should be served from backend
return /^\/static\/[a-z]+\.[0-9a-fA-F]+\.(css|js)$/.test(path);
},
target: redashBackend + "/",
changeOrigin: true,
secure: false
}
],
hot: isHotReloadingEnabled
},
performance: {
hints: false
}
};
if (process.env.DEV_SERVER_HOST) {
config.devServer.host = process.env.DEV_SERVER_HOST;
}
if (process.env.BUNDLE_ANALYZER) {
config.plugins.push(new BundleAnalyzerPlugin());
}
module.exports = maybeApplyOverrides(config);