The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/packages/grafana-plugin-configs/webpack.config.ts

302 lines
9.0 KiB

import CopyWebpackPlugin from 'copy-webpack-plugin';
import ESLintPlugin from 'eslint-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import path from 'path';
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack, { type Configuration } from 'webpack';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import VirtualModulesPlugin from 'webpack-virtual-modules';
import { DIST_DIR } from './constants.ts';
import { getPackageJson, getPluginJson, getEntries, hasLicense } from './utils.ts';
function skipFiles(f: string): boolean {
if (f.includes('/dist/')) {
// avoid copying files already in dist
return false;
}
if (f.includes('/tsconfig.json')) {
// avoid copying tsconfig.json
return false;
}
if (f.includes('/package.json')) {
// avoid copying package.json
return false;
}
if (f.includes('/project.json')) {
// avoid copying project.json
return false;
}
return true;
}
export type Env = {
[key: string]: true | string | Env;
};
const config = async (env: Env): Promise<Configuration> => {
const pluginJson = getPluginJson();
// Inject module
const virtualPublicPath = new VirtualModulesPlugin({
'node_modules/grafana-public-path.js': `
import amdMetaModule from 'amd-module';
__webpack_public_path__ =
amdMetaModule && amdMetaModule.uri
? amdMetaModule.uri.slice(0, amdMetaModule.uri.lastIndexOf('/') + 1)
: 'public/plugins/${pluginJson.id}/';
`,
});
const baseConfig: Configuration = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [import.meta.filename],
},
cacheDirectory: path.resolve(
import.meta.dirname,
'../../node_modules/.cache/webpack',
path.basename(process.cwd())
),
},
context: process.cwd(),
devtool: env.production ? 'source-map' : 'eval-source-map',
entry: await getEntries(),
externals: [
// Required for dynamic publicPath resolution
{ 'amd-module': 'module' },
'lodash',
'jquery',
'moment',
'slate',
'emotion',
'@emotion/react',
'@emotion/css',
'prismjs',
'slate-plain-serializer',
'@grafana/slate-react',
'react',
'react-dom',
'react-redux',
'redux',
'rxjs',
'rxjs/operators',
'react-router',
'd3',
'angular',
/^@grafana\/ui/i,
/^@grafana\/runtime/i,
/^@grafana\/data/i,
// Mark legacy SDK imports as external if their name starts with the "grafana/" prefix
({ request }, callback) => {
const prefix = 'grafana/';
const hasPrefix = (request?: string) => request?.indexOf(prefix) === 0;
const stripPrefix = (request?: string) => request?.substr(prefix.length);
if (hasPrefix(request)) {
return callback(undefined, stripPrefix(request));
}
callback();
},
],
mode: env.production ? 'production' : 'development',
module: {
rules: [
// This must come first in the rules array otherwise it breaks sourcemaps.
{
test: /module\.tsx?$/,
use: [
{
loader: 'imports-loader',
options: {
imports: `side-effects grafana-public-path`,
},
},
],
},
{
exclude: /(node_modules)/,
test: /\.[tj]sx?$/,
use: {
loader: 'swc-loader',
options: {
jsc: {
baseUrl: path.resolve(import.meta.dirname),
target: 'es2015',
loose: false,
parser: {
syntax: 'typescript',
tsx: true,
decorators: false,
dynamicImport: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
},
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.s[ac]ss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource',
generator: {
// Keep publicPath relative for host.com/grafana/ deployments
publicPath: `public/plugins/${pluginJson.id}/img/`,
outputPath: 'img/',
filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]',
},
},
{
test: /\.(woff|woff2|eot|ttf|otf)(\?v=\d+\.\d+\.\d+)?$/,
type: 'asset/resource',
generator: {
// Keep publicPath relative for host.com/grafana/ deployments
publicPath: `public/plugins/${pluginJson.id}/fonts/`,
outputPath: 'fonts/',
filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]',
},
},
],
},
output: {
clean: {
keep: new RegExp(`(.*?_(amd64|arm(64)?)(.exe)?|go_plugin_build_manifest)`),
},
filename: '[name].js',
library: {
type: 'amd',
},
path: path.resolve(process.cwd(), DIST_DIR),
publicPath: `public/plugins/${pluginJson.id}/`,
uniqueName: pluginJson.id,
},
optimization: {
minimize: Boolean(env.production),
minimizer: [
new TerserPlugin({
terserOptions: {
format: {
comments: (_, { type, value }) => type === 'comment2' && value.trim().startsWith('[create-plugin]'),
},
compress: {
drop_console: ['log', 'info'],
},
},
}),
],
},
plugins: [
virtualPublicPath,
// Insert create plugin version information into the bundle so Grafana will load from cdn with script tags.
new webpack.BannerPlugin({
banner: '/* [create-plugin] version: 5.22.0 */',
raw: true,
entryOnly: true,
}),
new CopyWebpackPlugin({
patterns: [
// To `compiler.options.output`
{ from: 'README.md', to: '.', force: true },
{ from: 'plugin.json', to: '.' },
{ from: hasLicense() ? 'LICENSE' : '../../../../../LICENSE', to: '.' }, // Point to Grafana License by default
{ from: 'CHANGELOG.md', to: '.', force: true },
{ from: '**/*.json', to: '.', filter: skipFiles }, // TODO<Add an error for checking the basic structure of the repo>
{ from: '**/*.svg', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
{ from: '**/*.png', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
{ from: '**/*.html', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
{ from: 'img/**/*', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
{ from: 'libs/**/*', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
{ from: 'static/**/*', to: '.', noErrorOnMissing: true, filter: skipFiles }, // Optional
],
}),
// Replace certain template-variables in the README and plugin.json
new ReplaceInFileWebpackPlugin([
{
dir: path.resolve(DIST_DIR),
files: ['plugin.json', 'README.md'],
rules: [
{
search: /\%VERSION\%/g,
replace: env.commit ? `${getPackageJson().version}-${env.commit}` : getPackageJson().version,
},
{
search: /\%TODAY\%/g,
replace: new Date().toISOString().substring(0, 10),
},
{
search: /\%PLUGIN_ID\%/g,
replace: pluginJson.id,
},
],
},
]),
...(env.development
? [
new ForkTsCheckerWebpackPlugin({
async: true,
issue: {
include: [{ file: '**/*.{ts,tsx}' }],
},
typescript: { configFile: path.join(process.cwd(), 'tsconfig.json') },
}),
new ESLintPlugin({
extensions: ['.ts', '.tsx'],
lintDirtyModulesOnly: true, // don't lint on start, only lint changed files
cacheLocation: path.resolve(
import.meta.dirname,
'../../node_modules/.cache/eslint-webpack-plugin',
path.basename(process.cwd()),
'.eslintcache'
),
configType: 'flat',
}),
]
: []),
],
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
unsafeCache: true,
},
Plugins: Always load decoupled frontend assets from builds (#81873) * Wip * Wip * Adapt to load external module * build: remove cloudmonitoring from built_in_plugins, clean up webpack output * chore(plugins): remove decoupled plugins from package.json deps * chore(codeowners): update file for nx.json * revert(webpack): put back path in config * build(frontend): use nx to run prod builds of decoupled plugins with yarn build * style(prometheus): run prettier-write to fix tsconfig.json * style(backend): remove unused subFile.isDistDir * revert(locales): remove formatting changes adding new line at end of files * chore(webpack): clean up dev output * build(nx): make grafana an nx project, bump lerna and nx * build(plugin-configs): move cache directory to node_modules * style(datasource-plugins): add eslint ignore for .gen.ts files * chore(codeowners): add frontend-ops as owner of project.json * build(webpack): add getDecoupledPlugins to automatically ignore when watching * ci(drone): skip nx cache when building frontend packages * style(ci): fix missing trailing comma * Revert "style(ci): fix missing trailing comma" This reverts commit 7520d41576e08c1f44c9bf04117250f7e52bdec5. * Revert "ci(drone): skip nx cache when building frontend packages" This reverts commit 46938883acaefb74d189e8c622eb2a13fd45cdfb. * feat(zipkin): remove from grafana core bundle * chore(npm): bump nx package to latest 18.0.8 * docs(dev-guide): add a note about what yarn start now builds --------- Co-authored-by: Andres Martinez <andres.martinez@grafana.com>
1 year ago
stats: 'minimal',
watchOptions: {
ignored: ['**/node_modules', '**/dist', '**/.yarn'],
},
};
if (env.stats) {
baseConfig.stats = 'normal';
baseConfig.plugins?.push(new BundleAnalyzerPlugin());
}
return baseConfig;
};
export default config;