How to bundle scripted dashboards with webpack?

I want to bundle a grafana’s scripted dashboard with webpack to modularize it.

As a start, I am trying to bundle a simple test script provided by grafana as an example. The sample example is working, but when I bundle it with webpack with a very minimal configuration, the output script is not working. I am getting the following error :

Script dashboard error TypeError: Cannot read property ‘rows’ of undefined

const path = require(‘path’);

// webpack.config.js

module.exports = {
  entry: './src/scripted.js',
  output: {
    filename: 'scripted_NEW.js',
    path: path.resolve(__dirname, 'dist'),
  },
  mode: 'production'
};

What am I missing ?

Reproduction repo

1 Like

Found a solution that’s involve a custom webpack plugin : https://github.com/ThomasKientz/grafana-webpack

1 Like

If anyone stumbles on this in the future and does not want to use this custom webpack configuration we solved this in a slightly different way.

For us to have many scripted dashboard JS files outputted from our strict TypeScript monorepo, we introduced a custom Rollup plugin with a hook function to clean up the bundled JS output file.

const rollupConfig = [
{
    input: `${scriptedDashboardsPath}/src/*.ts`,
    output: [
        {
            dir: `${scriptedDashboardsPath}/dist`,
            format: "cjs"
        }
    ],
    external: [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})],
    plugins: [
        multiInput({ relative: `${scriptedDashboardsPath}/src/` }),
        commonjs(),
        typescript(),
        cleanDist(),
        replaceModule()
    ]
}];

And for the custom function that will go through all of our different scripted dashboard bundled files and replace the needed object, set as a module.exports to the return method that Grafana needs.

const replaceModule = () => ({
  name: "replace Module",
  writeBundle() {
    const options = {
        files: "apps/grafana/scripted-dashboards/dist/*.js",
        from: "module.exports =",
        to: "return"
    };

    try {
        const results = replace.sync(options);
        results.forEach((file) => {
            if (file.hasChanged) console.log(`  - ${file.file.split("/dist/")[1]}\n`);
        });
    } catch (error) {
        console.error("Error occurred:", error);
    }
}});

This can of course be done with the different build hooks in any modern bundler, but we chose to use Rollup in this case.