powerbi-visuals-tools 7.0.2 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog.md +17 -0
- package/MCP.md +234 -0
- package/bin/pbiviz.js +12 -0
- package/eslint.config.mjs +12 -9
- package/lib/CertificateTools.js +1 -1
- package/lib/CommandManager.js +9 -1
- package/lib/ConsoleWriter.js +4 -0
- package/lib/FeatureManager.js +9 -3
- package/lib/Visual.js +6 -0
- package/lib/VisualGenerator.js +2 -2
- package/lib/VisualManager.js +22 -6
- package/lib/WebPackWrap.js +8 -4
- package/lib/features/AuthorInfo.js +14 -0
- package/lib/features/BaseFeature.js +1 -0
- package/lib/features/RenderingEvents.js +1 -0
- package/lib/features/index.js +2 -1
- package/lib/mcp/McpServer.js +122 -0
- package/lib/mcp/tools/availableApis.js +608 -0
- package/lib/mcp/tools/bestPractices.js +391 -0
- package/lib/mcp/tools/certification.js +380 -0
- package/lib/mcp/tools/visualInfo.js +133 -0
- package/lib/mcp/tools/vulnerabilities.js +211 -0
- package/lib/utils.js +27 -0
- package/package.json +22 -18
- package/templates/visuals/_global/.vscode/mcp.json +8 -0
- package/templates/visuals/default/src/visual.ts +17 -4
- package/templates/visuals/rhtml/src/visual.ts +27 -11
- package/templates/visuals/rvisual/src/visual.ts +34 -20
- package/templates/visuals/slicer/src/visual.ts +16 -4
- package/templates/visuals/table/src/visual.ts +14 -1
- package/tsconfig.json +2 -1
package/Changelog.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
This page contains information about changes to the PowerBI Visual Tools (pbiviz).
|
|
4
4
|
|
|
5
|
+
## 7.1.0
|
|
6
|
+
* Added MCP (Model Context Protocol) server support via `pbiviz mcp` command, providing AI-powered tools for visual development.
|
|
7
|
+
* Added MCP tools: available APIs, best practices, certification guidance, visual info, and vulnerabilities detection.
|
|
8
|
+
* Added MCP configuration template for new visuals (`.vscode/mcp.json`).
|
|
9
|
+
* Added unit tests for MCP tools.
|
|
10
|
+
* Updated packages.
|
|
11
|
+
|
|
12
|
+
## 7.0.4
|
|
13
|
+
* Treated renderingEvents issue as an error when certification-audit and certification-fix are used.
|
|
14
|
+
* Switched to a new webpack plugin.
|
|
15
|
+
* Added rendering events to the visual templates.
|
|
16
|
+
* Updated packages.
|
|
17
|
+
|
|
18
|
+
## 7.0.3
|
|
19
|
+
* Fixed missing validation for `author` object in pbiviz.json. The `author` object with `name` and `email` fields is now required.
|
|
20
|
+
* Updated packages.
|
|
21
|
+
|
|
5
22
|
## 7.0.2
|
|
6
23
|
* Changed source map generation to `eval-source-map`.
|
|
7
24
|
* Fixed load source map error.
|
package/MCP.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Power BI Visuals Tools - MCP Server
|
|
2
|
+
|
|
3
|
+
This document describes the MCP (Model Context Protocol) server integration for Power BI Custom Visual Tools, inspired by the Angular 21 MCP implementation.
|
|
4
|
+
|
|
5
|
+
## What is MCP?
|
|
6
|
+
|
|
7
|
+
Model Context Protocol (MCP) is an open protocol that allows AI assistants (like GitHub Copilot, Cursor, Claude) to interact with development tools directly. The MCP server exposes "tools" that AI can call to get context-aware information about your project.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### Option 1: Using VS Code with Copilot
|
|
12
|
+
|
|
13
|
+
1. Add this to your VS Code settings or create `.vscode/mcp.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"servers": {
|
|
18
|
+
"pbiviz": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "powerbi-visuals-tools", "mcp"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. Restart VS Code
|
|
27
|
+
3. In Copilot Chat, you can now ask questions like:
|
|
28
|
+
- "Check my visual for certification readiness"
|
|
29
|
+
- "What are the best practices for Power BI visuals?"
|
|
30
|
+
- "Show me available Power BI Visual APIs"
|
|
31
|
+
|
|
32
|
+
### Option 2: Using Cursor
|
|
33
|
+
|
|
34
|
+
Add to Cursor settings (`~/.cursor/mcp.json`):
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"pbiviz": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["-y", "powerbi-visuals-tools", "mcp"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Option 3: Run Manually (for testing)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cd /path/to/your/visual-project
|
|
51
|
+
pbiviz mcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The server runs on STDIO, so you won't see output, but it will respond to MCP requests.
|
|
55
|
+
|
|
56
|
+
## Available Tools
|
|
57
|
+
|
|
58
|
+
| Tool | Description | Local | Read-only |
|
|
59
|
+
|------|-------------|-------|-----------|
|
|
60
|
+
| `get_best_practices` | Returns best practice guidelines for Power BI visual development | ✅ | ✅ |
|
|
61
|
+
| `check_vulnerabilities` | Scans project for security vulnerabilities in dependencies and code | ✅ | ✅ |
|
|
62
|
+
| `prepare_certification` | Audits visual for Power BI certification readiness | ✅ | ✅ |
|
|
63
|
+
| `list_visual_info` | Returns info about current visual (name, GUID, API version, capabilities) | ✅ | ✅ |
|
|
64
|
+
| `get_available_apis` | Lists available Power BI Visual APIs with examples | ✅ | ✅ |
|
|
65
|
+
|
|
66
|
+
### Tool Details
|
|
67
|
+
|
|
68
|
+
#### `get_best_practices`
|
|
69
|
+
|
|
70
|
+
Returns comprehensive guidelines including:
|
|
71
|
+
- API version management
|
|
72
|
+
- Performance optimization tips
|
|
73
|
+
- Security guidelines
|
|
74
|
+
- Accessibility requirements
|
|
75
|
+
- Project structure recommendations
|
|
76
|
+
- Testing best practices
|
|
77
|
+
|
|
78
|
+
#### `check_vulnerabilities`
|
|
79
|
+
|
|
80
|
+
Analyzes:
|
|
81
|
+
- `package.json` dependencies for known vulnerable packages
|
|
82
|
+
- Source code for dangerous patterns (`eval()`, `innerHTML`, external fetch calls)
|
|
83
|
+
- Returns severity-categorized results (critical/high/medium/low/info)
|
|
84
|
+
|
|
85
|
+
#### `prepare_certification`
|
|
86
|
+
|
|
87
|
+
Checks:
|
|
88
|
+
- Required files (pbiviz.json, capabilities.json, package.json)
|
|
89
|
+
- Visual configuration (name, GUID, version format, API version)
|
|
90
|
+
- Capabilities (data roles, accessibility support)
|
|
91
|
+
- Assets (icon.png)
|
|
92
|
+
|
|
93
|
+
#### `list_visual_info`
|
|
94
|
+
|
|
95
|
+
Extracts from project files:
|
|
96
|
+
- Visual name, display name, GUID
|
|
97
|
+
- Version and API version
|
|
98
|
+
- Author information
|
|
99
|
+
- Defined data roles and format objects
|
|
100
|
+
- Supported features (highlight, keyboard focus, etc.)
|
|
101
|
+
- Dependencies list
|
|
102
|
+
|
|
103
|
+
#### `get_available_apis`
|
|
104
|
+
|
|
105
|
+
Provides documentation for:
|
|
106
|
+
- **Data APIs**: fetchMoreData, persistProperties
|
|
107
|
+
- **Formatting APIs**: colorPalette, getFormattingModel, high contrast
|
|
108
|
+
- **Interaction APIs**: selectionManager, tooltipService, contextMenu, drilldown
|
|
109
|
+
- **Utility APIs**: localization, storage, events, modal dialog
|
|
110
|
+
|
|
111
|
+
Accepts optional `category` parameter: `data`, `formatting`, `interaction`, `utility`, or `all`.
|
|
112
|
+
|
|
113
|
+
## Example Interactions
|
|
114
|
+
|
|
115
|
+
### With GitHub Copilot (Agent Mode)
|
|
116
|
+
|
|
117
|
+
**You:** "Is my visual ready for certification?"
|
|
118
|
+
|
|
119
|
+
**Copilot:** *calls `prepare_certification` tool*
|
|
120
|
+
|
|
121
|
+
> Based on the certification check, your visual has:
|
|
122
|
+
> - ✅ 12 passed checks
|
|
123
|
+
> - ⚠️ 2 warnings: Missing keyboard focus support, no README.md
|
|
124
|
+
> - ❌ 1 failed: API version 2.6.0 is outdated
|
|
125
|
+
>
|
|
126
|
+
> Recommendation: Update to API version 5.x and add `"supportsKeyboardFocus": true` to capabilities.json.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
**You:** "What APIs can I use for tooltips and selection?"
|
|
131
|
+
|
|
132
|
+
**Copilot:** *calls `get_available_apis` with category="interaction"*
|
|
133
|
+
|
|
134
|
+
> For tooltips, use `host.tooltipService.show()`:
|
|
135
|
+
> ```typescript
|
|
136
|
+
> host.tooltipService.show({
|
|
137
|
+
> dataItems: tooltipData,
|
|
138
|
+
> coordinates: [x, y]
|
|
139
|
+
> });
|
|
140
|
+
> ```
|
|
141
|
+
> For selection, use `selectionManager`:
|
|
142
|
+
> ```typescript
|
|
143
|
+
> const selectionManager = host.createSelectionManager();
|
|
144
|
+
> selectionManager.select(selectionId);
|
|
145
|
+
> ```
|
|
146
|
+
|
|
147
|
+
## How It Works
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
┌─────────────────┐ STDIO ┌─────────────────┐
|
|
151
|
+
│ VS Code / │ ◄───────────► │ pbiviz mcp │
|
|
152
|
+
│ Copilot │ (MCP) │ (MCP Server) │
|
|
153
|
+
└─────────────────┘ └─────────────────┘
|
|
154
|
+
│
|
|
155
|
+
▼
|
|
156
|
+
┌─────────────────┐
|
|
157
|
+
│ Visual Project │
|
|
158
|
+
│ - pbiviz.json │
|
|
159
|
+
│ - capabilities │
|
|
160
|
+
│ - package.json │
|
|
161
|
+
│ - src/ │
|
|
162
|
+
└─────────────────┘
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
1. VS Code starts `pbiviz mcp` as a subprocess
|
|
166
|
+
2. Copilot sends `ListTools` request → pbiviz returns available tools
|
|
167
|
+
3. When you ask a question, Copilot may call a tool
|
|
168
|
+
4. pbiviz executes the tool (reads project files, runs checks)
|
|
169
|
+
5. Results are returned to Copilot as structured text
|
|
170
|
+
6. Copilot presents the information in a helpful way
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
### Building from Source
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
git clone https://github.com/Microsoft/PowerBI-visuals-tools.git
|
|
178
|
+
cd PowerBI-visuals-tools
|
|
179
|
+
npm install
|
|
180
|
+
npm run build
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Testing MCP Server Locally
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# In your visual project directory:
|
|
187
|
+
node /path/to/PowerBI-visuals-tools/bin/pbiviz.js mcp
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Adding New Tools
|
|
191
|
+
|
|
192
|
+
1. Create a new file in `src/mcp/tools/`
|
|
193
|
+
2. Export a function that returns a string result
|
|
194
|
+
3. Register the tool in `src/mcp/McpServer.ts`
|
|
195
|
+
|
|
196
|
+
Example:
|
|
197
|
+
```typescript
|
|
198
|
+
// src/mcp/tools/myTool.ts
|
|
199
|
+
export function myTool(rootPath: string): string {
|
|
200
|
+
// Read files, perform checks, return markdown result
|
|
201
|
+
return "# My Tool Result\n...";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// In McpServer.ts
|
|
205
|
+
this.server.tool(
|
|
206
|
+
"my_tool",
|
|
207
|
+
"Description for AI",
|
|
208
|
+
{},
|
|
209
|
+
async () => ({
|
|
210
|
+
content: [{ type: "text", text: myTool(this.rootPath) }]
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Comparison with Angular 21 MCP
|
|
216
|
+
|
|
217
|
+
| Feature | Angular CLI | pbiviz (this implementation) |
|
|
218
|
+
|---------|-------------|------------------------------|
|
|
219
|
+
| Best practices | `get_best_practices` | `get_best_practices` |
|
|
220
|
+
| Project info | `list_projects` | `list_visual_info` |
|
|
221
|
+
| Code examples | `find_examples` | `get_available_apis` |
|
|
222
|
+
| Documentation search | `search_documentation` (online) | ❌ (offline only) |
|
|
223
|
+
| Migration tools | `onpush_zoneless_migration` | `prepare_certification` |
|
|
224
|
+
| Build/Run | `build`, `serve` | Use regular CLI commands |
|
|
225
|
+
|
|
226
|
+
## Requirements
|
|
227
|
+
|
|
228
|
+
- Node.js >= 18.0.0
|
|
229
|
+
- VS Code with GitHub Copilot (for Chat integration)
|
|
230
|
+
- Or Cursor / other MCP-compatible AI assistant
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
MIT - Microsoft Corporation
|
package/bin/pbiviz.js
CHANGED
|
@@ -111,4 +111,16 @@ pbiviz
|
|
|
111
111
|
CommandManager.package(options, rootPath);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
+
pbiviz
|
|
115
|
+
.command('mcp')
|
|
116
|
+
.description('Start MCP (Model Context Protocol) server for AI assistant integration')
|
|
117
|
+
.option('--init', 'Initialize MCP configuration in current project (.vscode/mcp.json)')
|
|
118
|
+
.action((options) => {
|
|
119
|
+
if (options.init) {
|
|
120
|
+
CommandManager.mcpInit(rootPath);
|
|
121
|
+
} else {
|
|
122
|
+
CommandManager.mcp(rootPath);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
114
126
|
program.parse(process.argv);
|
package/eslint.config.mjs
CHANGED
|
@@ -2,18 +2,21 @@ import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
|
|
2
2
|
import globals from "globals";
|
|
3
3
|
import tsParser from "@typescript-eslint/parser";
|
|
4
4
|
|
|
5
|
+
const __dirname = import.meta.dirname; // built-in, no imports needed
|
|
6
|
+
|
|
5
7
|
export default [
|
|
6
8
|
{
|
|
7
|
-
files: ["*.ts", "*tsx"],
|
|
8
9
|
ignores: [
|
|
9
|
-
"node_modules
|
|
10
|
-
"dist
|
|
11
|
-
"templates
|
|
12
|
-
"spec
|
|
13
|
-
"
|
|
14
|
-
"bin
|
|
15
|
-
"eslint.config.mjs",
|
|
10
|
+
"node_modules/**",
|
|
11
|
+
"dist/**",
|
|
12
|
+
"templates/**",
|
|
13
|
+
"spec/**",
|
|
14
|
+
"lib/**",
|
|
15
|
+
"bin/**",
|
|
16
16
|
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
files: ["**/*.ts", "**/*.tsx"],
|
|
17
20
|
plugins: {
|
|
18
21
|
"@typescript-eslint": typescriptEslint,
|
|
19
22
|
},
|
|
@@ -26,7 +29,7 @@ export default [
|
|
|
26
29
|
sourceType: "module",
|
|
27
30
|
parserOptions: {
|
|
28
31
|
project: "tsconfig.json",
|
|
29
|
-
tsconfigRootDir:
|
|
32
|
+
tsconfigRootDir: __dirname,
|
|
30
33
|
},
|
|
31
34
|
},
|
|
32
35
|
rules: {
|
package/lib/CertificateTools.js
CHANGED
package/lib/CommandManager.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createCertificate } from './CertificateTools.js';
|
|
2
2
|
import ConsoleWriter from './ConsoleWriter.js';
|
|
3
3
|
import VisualManager from './VisualManager.js';
|
|
4
|
+
import { startMcpServer, initMcpConfig } from './mcp/McpServer.js';
|
|
4
5
|
export default class CommandManager {
|
|
5
6
|
static async start(options, rootPath) {
|
|
6
7
|
const webpackOptions = {
|
|
@@ -50,12 +51,13 @@ export default class CommandManager {
|
|
|
50
51
|
verbose: options.verbose,
|
|
51
52
|
fix: options.fix,
|
|
52
53
|
};
|
|
54
|
+
const certificationMode = options.certificationAudit || options.certificationFix;
|
|
53
55
|
const visualManager = new VisualManager(rootPath);
|
|
54
56
|
const visual = await visualManager.prepareVisual(options.pbivizFile);
|
|
55
57
|
await visual.runLintValidation(lintOptions);
|
|
56
58
|
await visual.validateVisual(options.verbose);
|
|
57
59
|
await visual.initializeWebpack(webpackOptions)
|
|
58
|
-
.then(manager => manager.generatePackage(options.verbose));
|
|
60
|
+
.then(manager => manager.generatePackage(options.verbose, certificationMode));
|
|
59
61
|
}
|
|
60
62
|
static new({ force, template }, name, rootPath) {
|
|
61
63
|
const generateOptions = {
|
|
@@ -72,4 +74,10 @@ export default class CommandManager {
|
|
|
72
74
|
static async installCert() {
|
|
73
75
|
await createCertificate();
|
|
74
76
|
}
|
|
77
|
+
static async mcp(rootPath) {
|
|
78
|
+
await startMcpServer(rootPath);
|
|
79
|
+
}
|
|
80
|
+
static async mcpInit(rootPath) {
|
|
81
|
+
await initMcpConfig(rootPath);
|
|
82
|
+
}
|
|
75
83
|
}
|
package/lib/ConsoleWriter.js
CHANGED
|
@@ -42,6 +42,10 @@ export default class ConsoleWriter {
|
|
|
42
42
|
static blank() {
|
|
43
43
|
console.info(preferredChalk.reset(' '));
|
|
44
44
|
}
|
|
45
|
+
/** Outputs a separator line */
|
|
46
|
+
static separator() {
|
|
47
|
+
console.log(preferredChalk.white('--------------------------------'));
|
|
48
|
+
}
|
|
45
49
|
/**
|
|
46
50
|
* Outputs arguments with the "done" tag / colors
|
|
47
51
|
*
|
package/lib/FeatureManager.js
CHANGED
|
@@ -7,21 +7,27 @@ export var Status;
|
|
|
7
7
|
})(Status || (Status = {}));
|
|
8
8
|
export class FeatureManager {
|
|
9
9
|
features = Object.keys(features).map(key => features[key]);
|
|
10
|
-
validate(stage, sourceInstance) {
|
|
10
|
+
validate(stage, sourceInstance, certificationMode = false) {
|
|
11
11
|
const result = {
|
|
12
12
|
status: Status.Success,
|
|
13
13
|
logs: {
|
|
14
14
|
errors: [],
|
|
15
15
|
warnings: [],
|
|
16
16
|
info: [],
|
|
17
|
-
deprecation: []
|
|
17
|
+
deprecation: [],
|
|
18
|
+
certificationErrors: []
|
|
18
19
|
}
|
|
19
20
|
};
|
|
20
21
|
this.features
|
|
21
22
|
.filter(feature => feature.stage == stage)
|
|
22
23
|
.filter(feature => feature.visualFeatureType & sourceInstance.visualFeatureType)
|
|
23
24
|
.filter(feature => !feature.isSupported(sourceInstance))
|
|
24
|
-
.forEach(({ errorMessage, severity }) => {
|
|
25
|
+
.forEach(({ errorMessage, severity, certificationRequired }) => {
|
|
26
|
+
if (certificationMode && certificationRequired && severity === Severity.Warning) {
|
|
27
|
+
result.status = Status.Error;
|
|
28
|
+
result.logs.certificationErrors.push(errorMessage);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
25
31
|
switch (severity) {
|
|
26
32
|
case Severity.Error:
|
|
27
33
|
result.status = Status.Error;
|
package/lib/Visual.js
CHANGED
|
@@ -17,6 +17,12 @@ export class Visual {
|
|
|
17
17
|
isVisualVersionValid(length) {
|
|
18
18
|
return this.visualVersion.split(".").length === length;
|
|
19
19
|
}
|
|
20
|
+
isAuthorDefined() {
|
|
21
|
+
const author = this.config?.author;
|
|
22
|
+
const emailRegex = /^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$/;
|
|
23
|
+
return typeof author?.name === "string" && author.name.length > 0 &&
|
|
24
|
+
typeof author?.email === "string" && emailRegex.test(author.email);
|
|
25
|
+
}
|
|
20
26
|
getVisualFeatureType() {
|
|
21
27
|
const isMatrixSupported = this.capabilities?.dataViewMappings?.some(dataView => dataView.matrix);
|
|
22
28
|
const isSlicer = Boolean(this.capabilities?.objects?.general?.properties?.filter?.type?.filter);
|
package/lib/VisualGenerator.js
CHANGED
|
@@ -96,7 +96,7 @@ function fileExists(file) {
|
|
|
96
96
|
try {
|
|
97
97
|
fs.accessSync(file);
|
|
98
98
|
}
|
|
99
|
-
catch
|
|
99
|
+
catch {
|
|
100
100
|
return false;
|
|
101
101
|
}
|
|
102
102
|
return true;
|
|
@@ -120,7 +120,7 @@ function validTemplate(templateName) {
|
|
|
120
120
|
try {
|
|
121
121
|
fs.accessSync(path.join(VISUAL_TEMPLATES_PATH, templateName));
|
|
122
122
|
}
|
|
123
|
-
catch
|
|
123
|
+
catch {
|
|
124
124
|
return false;
|
|
125
125
|
}
|
|
126
126
|
return true;
|
package/lib/VisualManager.js
CHANGED
|
@@ -90,12 +90,15 @@ export default class VisualManager {
|
|
|
90
90
|
this.compiler = webpack(this.webpackConfig);
|
|
91
91
|
return this;
|
|
92
92
|
}
|
|
93
|
-
generatePackage(verbose = false) {
|
|
93
|
+
generatePackage(verbose = false, certificationMode = false) {
|
|
94
94
|
const callback = (err, stats) => {
|
|
95
95
|
this.parseCompilationResults(err, stats);
|
|
96
96
|
this.createPackageInstance();
|
|
97
|
-
const logs = this.validatePackage();
|
|
97
|
+
const { status, logs } = this.validatePackage(certificationMode);
|
|
98
98
|
this.outputResults(logs, verbose);
|
|
99
|
+
if (certificationMode && status === Status.Error) {
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
99
102
|
};
|
|
100
103
|
this.compiler.run(callback);
|
|
101
104
|
}
|
|
@@ -144,15 +147,15 @@ export default class VisualManager {
|
|
|
144
147
|
/**
|
|
145
148
|
* Validates the visual package
|
|
146
149
|
*/
|
|
147
|
-
validatePackage() {
|
|
150
|
+
validatePackage(certificationMode = false) {
|
|
148
151
|
const featureManager = new FeatureManager();
|
|
149
|
-
const { logs } = featureManager.validate(Stage.PostBuild, this.package);
|
|
150
|
-
return logs;
|
|
152
|
+
const { status, logs } = featureManager.validate(Stage.PostBuild, this.package, certificationMode);
|
|
153
|
+
return { status, logs };
|
|
151
154
|
}
|
|
152
155
|
/**
|
|
153
156
|
* Outputs the results of the validation
|
|
154
157
|
*/
|
|
155
|
-
outputResults({ errors, deprecation, warnings, info }, verbose) {
|
|
158
|
+
outputResults({ errors, deprecation, warnings, info, certificationErrors }, verbose) {
|
|
156
159
|
const headerMessage = {
|
|
157
160
|
error: `Visual doesn't support some features required for all custom visuals:`,
|
|
158
161
|
deprecation: `Some features are going to be required soon, please update the visual:`,
|
|
@@ -160,6 +163,9 @@ export default class VisualManager {
|
|
|
160
163
|
verboseInfo: `Visual can be improved by adding some features:`,
|
|
161
164
|
shortInfo: `Visual can be improved by adding ${info.length} more optional features.`
|
|
162
165
|
};
|
|
166
|
+
if (certificationErrors.length) {
|
|
167
|
+
this.outputCertificationSection(certificationErrors);
|
|
168
|
+
}
|
|
163
169
|
this.outputLogsWithHeadMessage(headerMessage.error, errors, Severity.Error);
|
|
164
170
|
this.outputLogsWithHeadMessage(headerMessage.deprecation, deprecation, Severity.Deprecation);
|
|
165
171
|
this.outputLogsWithHeadMessage(headerMessage.warning, warnings, Severity.Warning);
|
|
@@ -179,6 +185,16 @@ export default class VisualManager {
|
|
|
179
185
|
ConsoleWriter.error('Unable to load visual info. Please ensure the package is valid.');
|
|
180
186
|
}
|
|
181
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Outputs certification-required issues in a formatted section with separators
|
|
190
|
+
*/
|
|
191
|
+
outputCertificationSection(certificationErrors) {
|
|
192
|
+
ConsoleWriter.separator();
|
|
193
|
+
ConsoleWriter.info('Certification audit:');
|
|
194
|
+
certificationErrors.forEach(error => ConsoleWriter.error(error));
|
|
195
|
+
ConsoleWriter.error(`Found ${certificationErrors.length} certification issue(s). Fix them before submitting for certification.`);
|
|
196
|
+
ConsoleWriter.separator();
|
|
197
|
+
}
|
|
182
198
|
/**
|
|
183
199
|
* Creates a new visual
|
|
184
200
|
*/
|
package/lib/WebPackWrap.js
CHANGED
|
@@ -6,7 +6,6 @@ import util from 'util';
|
|
|
6
6
|
const exec = util.promisify(processExec);
|
|
7
7
|
import { exec as processExec } from 'child_process';
|
|
8
8
|
import lodashCloneDeep from 'lodash.clonedeep';
|
|
9
|
-
import ExtraWatchWebpackPlugin from 'extra-watch-webpack-plugin';
|
|
10
9
|
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
|
11
10
|
import { PowerBICustomVisualsWebpackPlugin, LocalizationLoader } from 'powerbi-visuals-webpack-plugin';
|
|
12
11
|
import ConsoleWriter from './ConsoleWriter.js';
|
|
@@ -180,9 +179,14 @@ export default class WebPackWrap {
|
|
|
180
179
|
analyzerMode: `static`
|
|
181
180
|
}));
|
|
182
181
|
}
|
|
183
|
-
this.webpackConfig.plugins.push(new PowerBICustomVisualsWebpackPlugin(pluginConfiguration),
|
|
184
|
-
|
|
185
|
-
|
|
182
|
+
this.webpackConfig.plugins.push(new PowerBICustomVisualsWebpackPlugin(pluginConfiguration), {
|
|
183
|
+
apply: (compiler) => {
|
|
184
|
+
compiler.hooks.afterCompile.tap('AddCapabilitiesWatch', (compilation) => {
|
|
185
|
+
const capabilitiesPath = path.resolve(process.cwd(), this.pbiviz.capabilities);
|
|
186
|
+
compilation.fileDependencies.add(capabilitiesPath);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
});
|
|
186
190
|
}
|
|
187
191
|
async configureLoaders({ fast = false, includeAllLocales = false }) {
|
|
188
192
|
this.webpackConfig.module.rules.push({
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
|
|
2
|
+
export default class AuthorInfo {
|
|
3
|
+
static featureName = "Author information";
|
|
4
|
+
static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/visual-project-structure#metadata-entries";
|
|
5
|
+
static errorMessage = `${this.featureName} - ${this.documentationLink}`;
|
|
6
|
+
static severity = Severity.Error;
|
|
7
|
+
static stage = Stage.PreBuild;
|
|
8
|
+
static visualFeatureType = VisualFeatureType.All;
|
|
9
|
+
static isSupported(visual) {
|
|
10
|
+
return visual.isAuthorDefined();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// The pbiviz.json schema validates author.name and author.email when present,
|
|
14
|
+
// but doesn't require the author object itself. This check fills that gap.
|
|
@@ -5,6 +5,7 @@ export default class RenderingEvents {
|
|
|
5
5
|
static severity = Severity.Warning;
|
|
6
6
|
static stage = Stage.PostBuild;
|
|
7
7
|
static visualFeatureType = VisualFeatureType.All;
|
|
8
|
+
static certificationRequired = true;
|
|
8
9
|
static errorMessage = `${this.featureName} - ${this.documentationLink}`;
|
|
9
10
|
static isSupported(packageInstance) {
|
|
10
11
|
const keywords = [".eventService", ".renderingStarted", ".renderingFinished"];
|
package/lib/features/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AdvancedEditMode from './AdvancedEditMode.js';
|
|
2
2
|
import AllowInteractions from './AllowInteractions.js';
|
|
3
3
|
import AnalyticsPane from './AnalyticsPane.js';
|
|
4
|
+
import AuthorInfo from './AuthorInfo.js';
|
|
4
5
|
import Bookmarks from './Bookmarks.js';
|
|
5
6
|
import ColorPalette from './ColorPalette.js';
|
|
6
7
|
import ConditionalFormatting from './ConditionalFormatting.js';
|
|
@@ -25,4 +26,4 @@ import TotalSubTotal from './TotalSubTotal.js';
|
|
|
25
26
|
import WarningIcon from './WarningIcon.js';
|
|
26
27
|
import APIVersion from './APIVersion.js';
|
|
27
28
|
import VisualVersion from './VisualVersion.js';
|
|
28
|
-
export { AdvancedEditMode, AllowInteractions, AnalyticsPane, Bookmarks, ColorPalette, ConditionalFormatting, ContextMenu, DrillDown, FetchMoreData, FileDownload, FormatPane, HighContrast, HighlightData, KeyboardNavigation, LandingPage, LaunchURL, Localizations, LocalStorage, ModalDialog, RenderingEvents, SelectionAcrossVisuals, SyncSlicer, Tooltips, TotalSubTotal, WarningIcon, APIVersion, VisualVersion };
|
|
29
|
+
export { AdvancedEditMode, AllowInteractions, AnalyticsPane, Bookmarks, ColorPalette, ConditionalFormatting, ContextMenu, DrillDown, FetchMoreData, FileDownload, FormatPane, HighContrast, HighlightData, KeyboardNavigation, LandingPage, LaunchURL, Localizations, LocalStorage, ModalDialog, RenderingEvents, SelectionAcrossVisuals, SyncSlicer, Tooltips, TotalSubTotal, WarningIcon, APIVersion, VisualVersion, AuthorInfo };
|