mirror of
https://github.com/grafana/grafana.git
synced 2026-06-03 13:43:38 -04:00
* 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
139 lines
4.6 KiB
TypeScript
139 lines
4.6 KiB
TypeScript
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
|
|
import { EsbuildPlugin } from 'esbuild-loader';
|
|
import { createRequire } from 'node:module';
|
|
import path from 'node:path';
|
|
import webpack, { type Configuration } from 'webpack';
|
|
import WebpackAssetsManifest from 'webpack-assets-manifest';
|
|
import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
|
|
import { merge } from 'webpack-merge';
|
|
|
|
import FeatureFlaggedSRIPlugin from './plugins/FeatureFlaggedSriPlugin.ts';
|
|
import { esbuildOptions } from './rules.ts';
|
|
import common, { type Env } from './webpack.common.ts';
|
|
|
|
// SRI plugin has broken esm builds so we use require.
|
|
// https://github.com/waysact/webpack-subresource-integrity/issues/236
|
|
const require = createRequire(import.meta.url);
|
|
const { SubresourceIntegrityPlugin } = require('webpack-subresource-integrity');
|
|
|
|
export default (env: Env = {}) => {
|
|
const prodConfig: Configuration = {
|
|
mode: 'production',
|
|
devtool: process.env.NO_SOURCEMAP === '1' ? false : 'source-map',
|
|
|
|
output: {
|
|
crossOriginLoading: 'anonymous',
|
|
},
|
|
|
|
optimization: {
|
|
nodeEnv: 'production',
|
|
minimize: Number(env.noMinify) !== 1,
|
|
minimizer: [new EsbuildPlugin(esbuildOptions), new CssMinimizerPlugin()],
|
|
runtimeChunk: 'single',
|
|
splitChunks: {
|
|
chunks: 'all',
|
|
minChunks: 1,
|
|
cacheGroups: {
|
|
moment: {
|
|
test: /[\\/]node_modules[\\/]moment[\\/].*[jt]sx?$/,
|
|
chunks: 'initial',
|
|
priority: 20,
|
|
enforce: true,
|
|
},
|
|
defaultVendors: {
|
|
test: /[\\/]node_modules[\\/].*[jt]sx?$/,
|
|
chunks: 'initial',
|
|
priority: -10,
|
|
reuseExistingChunk: true,
|
|
enforce: true,
|
|
},
|
|
default: {
|
|
priority: -20,
|
|
chunks: 'all',
|
|
test: /.*[jt]sx?$/,
|
|
reuseExistingChunk: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
// enable persistent cache for faster builds
|
|
cache:
|
|
Number(env.noMinify) === 1
|
|
? false
|
|
: {
|
|
type: 'filesystem',
|
|
name: 'grafana-default-production',
|
|
buildDependencies: {
|
|
config: [import.meta.filename],
|
|
},
|
|
},
|
|
|
|
plugins: [
|
|
new SubresourceIntegrityPlugin(),
|
|
new FeatureFlaggedSRIPlugin(),
|
|
/**
|
|
* I know we have two manifest plugins here.
|
|
* WebpackManifestPlugin was only used in prod before and does not support integrity hashes
|
|
*/
|
|
new WebpackAssetsManifest({
|
|
entrypoints: true,
|
|
integrity: true,
|
|
integrityHashes: ['sha384', 'sha512'],
|
|
publicPath: true,
|
|
// This transform filters down the assets to only include the ones that are part of the entrypoints
|
|
// this is all that the backend requires.
|
|
transform(assets, manifest) {
|
|
const entrypointsKey = manifest.options.entrypointsKey;
|
|
if (typeof entrypointsKey !== 'string') {
|
|
return assets;
|
|
}
|
|
|
|
const entrypointsValue = assets[entrypointsKey];
|
|
const entrypointAssets = isEntrypointsMap(entrypointsValue)
|
|
? Object.values(entrypointsValue).flatMap((entry) => [
|
|
...(entry.assets.js || []),
|
|
...(entry.assets.css || []),
|
|
])
|
|
: [];
|
|
|
|
const filteredAssets = Object.entries(assets).filter(([assetFileName]) => {
|
|
const asset = assets[assetFileName];
|
|
return isAssetEntry(asset) && entrypointAssets.includes(asset.src);
|
|
});
|
|
const result = Object.fromEntries(filteredAssets);
|
|
result[entrypointsKey] = entrypointsValue;
|
|
|
|
return result;
|
|
},
|
|
output: env.react19 ? 'assets-manifest-react19.json' : 'assets-manifest.json',
|
|
}),
|
|
new WebpackManifestPlugin({
|
|
fileName: path.join(process.cwd(), env.react19 ? 'manifest-react19.json' : 'manifest.json'),
|
|
filter: (file) => !file.name.endsWith('.map'),
|
|
}),
|
|
function () {
|
|
this.hooks.done.tap('Done', function (stats) {
|
|
if (stats.compilation.errors && stats.compilation.errors.length) {
|
|
console.log(stats.compilation.errors);
|
|
process.exit(1);
|
|
}
|
|
});
|
|
},
|
|
],
|
|
};
|
|
|
|
return merge(common(env), prodConfig);
|
|
};
|
|
|
|
interface EntrypointAssets {
|
|
assets: { js?: string[]; css?: string[] };
|
|
}
|
|
|
|
function isEntrypointsMap(value: unknown): value is Record<string, EntrypointAssets> {
|
|
return typeof value === 'object' && value !== null;
|
|
}
|
|
|
|
function isAssetEntry(value: unknown): value is { src: string } {
|
|
return typeof value === 'object' && value !== null && 'src' in value;
|
|
}
|