This plugin allows developing custom visuals by using webpack to build a visual package.
Provides following functionality:
Plugin config description
const defaultOptions = {
visual: {
name: "Visual name",
displayName: "Visual name for displaying in visuals panel",
guid: `Unique GUID for the visual (generated by plugin)`,
visualClassName: "Visual class name, it is used by visual plugin to create instance of the visual",
version: "Visual version",
description: "Visual description",
supportUrl: "URL for support"
},
author: "Author of the visual",
apiVersion: "API version"
capabilities: {
// Visual capabilities
},
iconImage: "Icon file as base64 string",
devMode: "development mode",
packageOutPath: "location to create *.pbiviz file",
generateResources: "it is used --resources flag in pbiviz tools",
generatePbiviz: "it is used by --no-pbiviz flag in pbiviz tools"
};
Install all required libraries to build a visual
npm i webpack webpack-cli powerbi-visuals-webpack-plugin mini-css-extract-plugin webpack-bundle-analyzer extra-watch-webpack-plugin ts-loader json-loader less-loader less css-loader webpack-dev-server --save-dev
Install optional libraries to build JSX files
If you aren't going to build JSX files, then you can skip this step and delete all babel in section 5.
npm i @babel/preset-react @babel/runtime @babel/runtime-corejs3 @babel/core @babel/preset-env babel-loader --save-dev
Install the latest version of API package
npm i powerbi-visuals-api --save
Create SSL certificates (optional)
You need generate SSL certificates manually or copy files from powerbi-visuals-tools instance.
Read more about certificates in documentation.
Also, you can use certificates from powerbi-visuals-tools
or use autogenerated certificates by webpack-dev-server
. Just skip section 4 to use webpack-dev-server certificates.
4.1 Run the following command:
npm i powerbi-visuals-tools@latest --save-dev
to install the latest version of tools
4.2 create script command to generate certificate.
Add into scripts
section of package.json
command "cert": "pbiviz --install-cert"
:
...
"scripts": {
"cert": "pbiviz --install-cert"
},
...
Execute command npm run cert
. You should get message:
error Certificate not found. The new certificate will be generated
info Certificate generated. Location is <visual root>\node_modules\powerbi-visuals-tools\certs\PowerBICustomVisualTest_public.pfx. Passphrase is '<YOUR_PASSPHRASE>'
Apply path node_modules\powerbi-visuals-tools\certs\PowerBICustomVisualTest_public.pfx
in webpack.config.js at https
section of devServer
parameters:
...
https: {
pfx: fs.readFileSync(path.join(__dirname, `node_modules/powerbi-visuals-tools/certs/PowerBICustomVisualTest_public.pfx`)), // for windows
passphrase: "<YOUR_PASSPHRASE>",
requestCert: true
},
...
Use sample of config webpack 5. (copy into webpack.config.js
)
const path = require("path");
// webpack
const webpack = require("webpack");
const { PowerBICustomVisualsWebpackPlugin, LocalizationLoader } = require('powerbi-visuals-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const ExtraWatchWebpackPlugin = require("extra-watch-webpack-plugin");
// api configuration
const powerbiApi = require("powerbi-visuals-api");
// visual configuration json path
const pbivizPath = "./pbiviz.json";
const pbivizFile = require(path.join(__dirname, pbivizPath));
// the visual capabilities content
const capabilitiesPath = "./capabilities.json";
const capabilities = require(path.join(__dirname, capabilitiesPath));
const pluginLocation = "./.tmp/precompile/visualPlugin.ts"; // Path to visual plugin file, the file generated by the webpack-plugin
const visualSourceLocation = "../../src/visual" // This path is used inside of the generated plugin, so it depends on pluginLocation
const statsLocation = "../../webpack.statistics.html";
const babelOptions = {
presets: [
[
require.resolve('@babel/preset-env'),
{
"targets": {
"ie": "11"
},
useBuiltIns: "entry",
corejs: 3,
modules: false
}
],
"@babel/preset-react" // required for jsx files
],
sourceType: "unambiguous", // tell to babel that the project can contains different module types, not only es2015 modules
cacheDirectory: path.join(".tmp", "babelCache") // path for cache files
};
const isProduction = true
module.exports = {
entry: {
"visual.js": pluginLocation,
},
target: "web",
devtool: "source-map",
mode: isProduction ? "production" : "development",
optimization: {
minimize: isProduction, // enable minimization for create *.pbiviz file less than 2 Mb, can be disabled for dev mode
},
performance: {
maxEntrypointSize: 1024000,
maxAssetSize: 1024000
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
include: /powerbi-visuals-|src|precompile(\\|\/)visualPlugin.ts/,
use: [
{
loader: require.resolve('babel-loader'),
options: babelOptions
},
{
loader: "ts-loader",
options: {
transpileOnly: false,
experimentalWatchApi: false
}
}
],
},
{
test: /(\.js)x|\.js$/,
use: [
{
loader: require.resolve('babel-loader'),
options: babelOptions
}
]
},
{
test: /\.json$/,
loader: 'json-loader',
type: "javascript/auto"
},
{
test: /(\.less)|(\.css)$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader'
},
{
loader: 'less-loader',
options: {
lessOptions: {
paths: [path.resolve(__dirname, 'node_modules')]
}
}
}
]
},
{
test: /\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp|gif|svg|eot)$/i,
type: 'asset/inline'
},
{
test: /powerbiGlobalizeLocales\.js$/,
loader: LocalizationLoader
}
],
},
externals: { "powerbi-visuals-api": "null" },
resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js", ".css"],
},
output: {
clean: true,
path: path.join(__dirname, ".tmp", "drop"),
publicPath: 'assets',
filename: "[name]",
library: pbivizFile.visual.guid,
libraryTarget: "var",
},
devServer: {
static: {
directory: path.join(__dirname, ".tmp", "drop"), // path with assets generated by webpack plugin
publicPath: '/assets/'
},
compress: true, // enable gzip compression
port: 8080, // dev server port
hot: false,
server: { // cert files for dev server
type: "https",
options: {
// keep it commented to use webpack generated certificate
// key: path.join(__dirname, "certs","PowerBICustomVisualTest_public.key"), // for darwin, linux
// cert: path.join(__dirname, "certs", "PowerBICustomVisualTest_public.cer"), // for darwin, linux
// pfx: fs.readFileSync(path.join(__dirname, "certs", "PowerBICustomVisualTest_public.pfx")), // for windows
// passphrase: "5031595470751755"
// requestCert: true,
}
},
headers: {
"access-control-allow-origin": "*",
"cache-control": "public, max-age=0",
}
},
plugins: [
new MiniCssExtractPlugin({
filename: "visual.css",
chunkFilename: "[id].css"
}),
new BundleAnalyzerPlugin({ // adds the ability to analyze the size of the bundle
reportFilename: statsLocation,
openAnalyzer: false,
analyzerMode: `static`
}),
new PowerBICustomVisualsWebpackPlugin({ // custom visuals plugin instance with options
...pbivizFile,
capabilities,
visualSourceLocation,
pluginLocation,
apiVersion: powerbiApi.version,
capabilitiesSchema: powerbiApi.schemas.capabilities,
dependenciesSchema: powerbiApi.schemas.dependencies,
devMode: false,
generatePbiviz: true,
generateResources: isProduction,
modules: true,
packageOutPath: path.join(__dirname, "dist"),
}),
new ExtraWatchWebpackPlugin({
files: [pbivizPath, capabilitiesPath],
}),
new webpack.WatchIgnorePlugin({ // visual plugin regenerates with the visual source, but it does not require relaunching dev server
paths: [
path.join(__dirname, pluginLocation),
"./.tmp/**/*.*",
"./.tmp/**/**/*.*"
]
}),
new webpack.ProvidePlugin({
define: "fakeDefine",
}),
],
};
Add new script to build a visual package
Add new command "package": "webpack"
into scripts
section of package.json
:
"scripts": {
"cert": "pbiviz --install-cert",
"package": "webpack"
}
Run npm run package
command to create visual package.
Install webpack-dev-server:
npm i webpack-dev-server --save-dev
Add command "start": "webpack serve"
into scripts
section of package.json
:
"scripts": {
"cert": "pbiviz --install-cert",
"package": "webpack",
"start": "webpack serve"
}
Run command npm run start
to start dev server.
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.