grafana/scripts/webpack/webpack.common.ts
Jack Westbrook 0a10a5d133
Build: Refactor Webpack config to TS (#121181)
* build(webpack): add tsconfig.json for Node strip-types compliance

* build(webpack): extract shared esbuild options to esbuild.ts

* build(webpack): add package.json to declare ESM module type

* build(webpack): convert sass.rule to TypeScript

* build(webpack): convert CorsWorkerPlugin to TypeScript

* build(webpack): convert FeatureFlaggedSriPlugin to TypeScript

* build(webpack): convert webpack.common to TypeScript, add theme entries

* build(webpack): convert webpack.dev to TypeScript, remove esbuild duplication

* build(webpack): convert webpack.prod to TypeScript, remove esbuild duplication

* build(webpack): fix TypeScript types in webpack.prod transform callback

* build(webpack): convert webpack.stats to TypeScript

* build(webpack): update scripts to use TypeScript webpack configs

* build(webpack): simplify env-util to use import.meta.dirname directly

* build(webpack): tidy up plugins

* build(webpack): move rules for ts and sass into single module

* build(webpack): consolidate shared config into common, move splitChunks to prod

- Move MiniCssExtractPlugin, esbuildRule and sassRule into common so both
  dev and prod configs share them without duplication
- Move splitChunks/runtimeChunk optimisation to webpack.prod only (not
  needed in dev)
- Use require() for SubresourceIntegrityPlugin to work around broken ESM
  build (waysact/webpack-subresource-integrity#236)
- Refactor conditional plugin logic in dev from ternary to if-blocks

* build(webpack): remove dead import and clarify webpack destructure pattern

- Remove unused MiniCssExtractPlugin import from webpack.prod (moved to common)
- Add comment explaining why DefinePlugin/EnvironmentPlugin are destructured
  from the default webpack import rather than using named ESM imports

* style(webpack): reorder consts

* chore(env-util): fix up env-util and webpack configs so tests continue to run

* refactor(env-util): accept grafanaRoot param instead of relying on __dirname

Removes the global.__dirname mutation hack in webpack.common.ts by making
the grafana root path an explicit argument to getEnvConfig. Each caller
resolves its own root and passes it in, removing the implicit path-depth
contract and the CJS/ESM compatibility workaround.

* build(webpack): remove unused angular chunk group
2026-04-28 12:45:31 +02:00

146 lines
4.8 KiB
TypeScript

import CopyWebpackPlugin from 'copy-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import webpack, { type Configuration } from 'webpack';
import { getEnvConfig } from '../cli/env-util.ts';
import CorsWorkerPlugin from './plugins/CorsWorkerPlugin.ts';
import { esbuildRule, sassRule } from './rules.ts';
const require = createRequire(import.meta.url);
const grafanaRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
const envConfig = getEnvConfig(grafanaRoot);
export type Env = Record<string, string | true | undefined>;
export default (env: Env = {}): Configuration => ({
target: 'web',
entry: {
app: './public/app/index.ts',
swagger: './public/swagger/index.tsx',
boot: {
import: './public/boot/index.ts',
runtime: false,
},
dark: './public/sass/grafana.dark.scss',
light: './public/sass/grafana.light.scss',
},
experiments: {
// Required to load WASM modules.
asyncWebAssembly: true,
},
output: {
clean: env.react19 ? false : true,
path: path.resolve(import.meta.dirname, '../../public/build'),
filename: (pathData) => {
if (pathData.chunk?.name === 'boot') {
return '[name].js';
}
return env.react19 ? '[name]-react19.[contenthash].js' : '[name].[contenthash].js';
},
chunkFilename: env.react19 ? '[name]-react19.[contenthash].js' : '[name].[contenthash].js',
publicPath: 'public/build/',
},
resolve: {
conditionNames: ['@grafana-app/source', '...'],
extensions: ['.ts', '.tsx', '.es6', '.js', '.json', '.svg'],
alias: {
// some of data source plugins use global Prism object to add the language definition
// we want to have same Prism object in core and in grafana/ui
prismjs: require.resolve('prismjs'),
// due to our webpack configuration not understanding package.json `exports`
// correctly we must alias this package to the correct file
// the alternative to this alias is to copy-paste the file into our
// source code and miss out in updates
'@locker/near-membrane-dom/custom-devtools-formatter': require.resolve(
'@locker/near-membrane-dom/custom-devtools-formatter.js'
),
},
modules: [
// default value
'node_modules',
// required for grafana enterprise resolution
path.resolve('node_modules'),
// required to for 'bare' imports (like 'app/core/utils' etc)
path.resolve('public'),
],
fallback: {
buffer: false,
fs: false,
stream: false,
http: false,
https: false,
string_decoder: false,
},
},
ignoreWarnings: [
/export .* was not found in/,
{
module: /@kusto\/language-service\/bridge\.min\.js$/,
message: /^Critical dependency: the request of a dependency is an expression$/,
},
],
plugins: [
...(env.react19
? [
new webpack.NormalModuleReplacementPlugin(/^react$/, (resource) => {
resource.request = resource.request.replace('react', 'react-19');
}),
new webpack.NormalModuleReplacementPlugin(/^react-dom/, (resource) => {
resource.request = resource.request.replace('react-dom', 'react-dom-19');
}),
new webpack.NormalModuleReplacementPlugin(/^react\/jsx-runtime$/, (resource) => {
resource.request = resource.request.replace('react/jsx-runtime', 'react-19/jsx-runtime');
}),
new webpack.NormalModuleReplacementPlugin(/^react\/jsx-dev-runtime/, (resource) => {
resource.request = resource.request.replace('react/jsx-dev-runtime', 'react-19/jsx-dev-runtime');
}),
]
: []),
new CorsWorkerPlugin(),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
}),
new CopyWebpackPlugin({
patterns: [
{ from: 'public/img', to: 'img' },
{ from: 'public/maps', to: 'maps' },
{ from: 'public/gazetteer', to: 'gazetteer' },
],
}),
new MiniCssExtractPlugin({
filename: env.react19 ? 'grafana.[name]-react19.[contenthash].css' : 'grafana.[name].[contenthash].css',
}),
new webpack.EnvironmentPlugin(envConfig),
],
module: {
rules: [
esbuildRule,
sassRule,
{
test: require.resolve('jquery'),
loader: 'expose-loader',
options: {
exposes: ['$', 'jQuery'],
},
},
{
test: /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
type: 'asset/resource',
generator: { filename: 'static/img/[name].[hash:8][ext]' },
},
{
// Required for msagl library (used in Nodegraph panel) to work
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
},
],
},
});