powerbi-visuals-tools 5.0.3 → 5.2.0-beta.1

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.
Files changed (44) hide show
  1. package/Changelog.md +17 -1
  2. package/bin/pbiviz.js +10 -6
  3. package/certs/PowerBICustomVisualTest_private.key +26 -26
  4. package/certs/PowerBICustomVisualTest_public.crt +17 -17
  5. package/lib/CertificateTools.js +2 -2
  6. package/lib/CommandManager.js +8 -2
  7. package/lib/FeatureManager.js +43 -0
  8. package/lib/Package.js +46 -0
  9. package/lib/Visual.js +34 -0
  10. package/lib/VisualManager.js +74 -1
  11. package/lib/WebPackWrap.js +64 -51
  12. package/lib/features/APIVersion.js +16 -0
  13. package/lib/features/AdvancedEditMode.js +11 -0
  14. package/lib/features/AllowInteractions.js +11 -0
  15. package/lib/features/AnalyticsPane.js +16 -0
  16. package/lib/features/BaseFeature.js +11 -0
  17. package/lib/features/Bookmarks.js +11 -0
  18. package/lib/features/ColorPalette.js +11 -0
  19. package/lib/features/ConditionalFormatting.js +11 -0
  20. package/lib/features/ContextMenu.js +11 -0
  21. package/lib/features/DrillDown.js +15 -0
  22. package/lib/features/ESLint.js +11 -0
  23. package/lib/features/FeatureTypes.js +23 -0
  24. package/lib/features/FetchMoreData.js +21 -0
  25. package/lib/features/FileDownload.js +11 -0
  26. package/lib/features/FormatPane.js +11 -0
  27. package/lib/features/HighContrast.js +11 -0
  28. package/lib/features/HighlightData.js +11 -0
  29. package/lib/features/KeyboardNavigation.js +11 -0
  30. package/lib/features/LandingPage.js +11 -0
  31. package/lib/features/LaunchURL.js +11 -0
  32. package/lib/features/LocalStorage.js +11 -0
  33. package/lib/features/Localizations.js +11 -0
  34. package/lib/features/ModalDialog.js +11 -0
  35. package/lib/features/RenderingEvents.js +12 -0
  36. package/lib/features/SelectionAcrossVisuals.js +11 -0
  37. package/lib/features/SyncSlicer.js +11 -0
  38. package/lib/features/Tooltips.js +11 -0
  39. package/lib/features/TotalSubTotal.js +11 -0
  40. package/lib/features/VisualVersion.js +12 -0
  41. package/lib/features/WarningIcon.js +11 -0
  42. package/lib/features/index.js +29 -0
  43. package/lib/webpack.config.js +0 -5
  44. package/package.json +6 -5
package/Changelog.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  This page contains information about changes to the PowerBI Visual Tools (pbiviz).
4
4
 
5
+ ## 5.2.0
6
+ * Integrated PAC validation
7
+
8
+ ## 5.1.0
9
+ * New flag `--skip-api` to skip verifying api version. It might produce different errors in visual, so use it only in some specific cases (ex. installing something during the build process brakes packages managed by monorepo managers).
10
+ * New flag `--all-locales` to disable optimization using localization loader. It's recommended not to use this flag because all locales take a huge amount of package size. If you need just a few of them follow [this guide](https://learn.microsoft.com/en-us/power-bi/developer/visuals/localization?tabs=English#step-5---add-a-resources-file-for-each-language). In this case, only declared in stringResources locales will be added to your visual package.
11
+
12
+ ## 5.0.3
13
+ * Now option `--install-cert` is command. The new usage is `pbiviz install-cert` **⚠**
14
+
5
15
  ## 5.0.3
6
16
  * Now option `--install-cert` is command. New usage is `pbiviz install-cert`
7
17
  * Fixed bug with the incorrect detection of the installed API version
@@ -20,6 +30,7 @@ This page contains information about changes to the PowerBI Visual Tools (pbiviz
20
30
  * Migrated to NodeJS 18.0 **⚠**
21
31
 
22
32
  ## 4.3.2
33
+ * `options` in `Visual.constructor()` is optional. It's made to match PowerBI interface and to support strict mode
23
34
  * LocalizationLoader has been moved to `powerbi-visuals-webpack-plugin`
24
35
 
25
36
  ## 4.3.1
@@ -36,8 +47,13 @@ This page contains information about changes to the PowerBI Visual Tools (pbiviz
36
47
  * Fixed vulnerabilities
37
48
  * Updated `powerbi-visuals-webpack-plugin` to 3.2.0
38
49
 
50
+
51
+ ### **⚠ BREAKING CHANGES**
39
52
  ## 4.1.0
40
- * Added loader to reduce localizations size. REQUIRES `powerbi-visuals-utils-formattingutils` version 5.1 and higher
53
+ * Added loader to reduce localizations size. REQUIRES `powerbi-visuals-utils-formattingutils` version 5.1 and higher.
54
+
55
+ Now loader deletes all unused in stringResources folder locales.
56
+
41
57
  * Fixed vulnerabilities
42
58
 
43
59
  ## 4.0.9
package/bin/pbiviz.js CHANGED
@@ -44,9 +44,9 @@ const pbiviz = program
44
44
  pbiviz
45
45
  .command('new')
46
46
  .usage("<argument> [options]")
47
- .argument('<name>', 'name of new visual')
48
- .option('-f, --force', 'force creation (overwrites folder if exists)')
49
- .addOption(new Option('-t, --template [template]', 'use a specific template')
47
+ .argument('<name>', 'Name of new visual')
48
+ .option('-f, --force', 'Force creation (overwrites folder if exists)')
49
+ .addOption(new Option('-t, --template [template]', 'Use a specific template')
50
50
  .choices(['default', 'table', 'slicer', 'rvisual', 'rhtml', 'circlecard'])
51
51
  .default('default')
52
52
  )
@@ -71,9 +71,11 @@ pbiviz
71
71
  pbiviz
72
72
  .command('start')
73
73
  .usage('[options]')
74
- .option('-p, --port [port]', 'set the port listening on')
75
- .option('-d, --drop', 'drop outputs into output folder')
74
+ .option('-p, --port [port]', 'Set the port listening on')
75
+ .option('-d, --drop', 'Drop outputs into output folder')
76
76
  .option('--no-stats', "Doesn't generate statistics files")
77
+ .option('--skip-api', "Skips powerbi-visuals-api verifying")
78
+ .option('-l, --all-locales', "Keeps all locale files in the package. By default only used inside stringResources folder locales are included.")
77
79
  .action(async (options) => {
78
80
  CommandManager.start(options, rootPath);
79
81
  });
@@ -85,6 +87,8 @@ pbiviz
85
87
  .option('--no-pbiviz', "Doesn't produce a pbiviz file (must be used in conjunction with resources flag)")
86
88
  .option('--no-minify', "Doesn't minify the js in the package (useful for debugging)")
87
89
  .option('--no-stats', "Doesn't generate statistics files")
90
+ .option('--skip-api', "Skips powerbi-visuals-api verifying")
91
+ .option('-l, --all-locales', "Keeps all locale files in the package. By default only used inside stringResources folder locales are included.")
88
92
  .addOption(new Option('-c, --compression <compressionLevel>', "Enables compression of visual package")
89
93
  .choices(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
90
94
  .default('6')
@@ -93,4 +97,4 @@ pbiviz
93
97
  CommandManager.package(options, rootPath);
94
98
  });
95
99
 
96
- program.parse(process.argv);
100
+ program.parse(process.argv);
@@ -1,28 +1,28 @@
1
1
  -----BEGIN PRIVATE KEY-----
2
- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC155Q6mq/fmI37
3
- F9qnGiky6TA0zLOAQV/k0dQbx0vz9+33dO7h+Hnjyu6YUOV1YvFzOWg81nfCUrT3
4
- ccoOUQmahanypuvW5cqtopXH0b3Qsfau+whA5PM6ozR3cV4HrxQa00hLe7GFDZWn
5
- oDJsr9QpZ0bWTmBm20kZ9Rg4NegUQb9LV9Ko0HMbC4zdnRGb2HndlNxnECd1qFCL
6
- duPSRRoyfEi6sOaLApBRr2gbyKi33AKGnEpH2N8Nll1dYWjjC5tuz62LzNSzT8dW
7
- N9d85HQsxp0+isHrss02+dCF/gBsnhr4yScmgIY49XFlpPdqKySETPb0TBKx2XA+
8
- Eil/XeaDAgMBAAECggEASAQI4TLAkks5WsFU5Xcr/GXpmd246vv21QZlbSH35fyw
9
- D5oRwRC4YQDEeTT9wv2LDc0agYt/GkXYM2avUf+vR6fHYrPHBusu1JNbrSJSfb8X
10
- JKtp1xdNXFeku+z8Bxq/YOdo5clwvHZmW1jG3dS2sDWdC9c3v0DtrgMktYxR90Jt
11
- XWa4q2C/U0qmjppMmXwH7NCoHjflIGUPq3wnPYO4VTjI7xQUDTnrFt3WefDJPB1C
12
- 7Qj/uFIZhG1aLGUx5rCrZ6Shwt61kB1yEHHRGEvaccexk7Hj93MFFPxiY2sRXI67
13
- 8RWaeUZVl1dnlGTJc4ceBp1JQz8unw6Z8bVX90OrsQKBgQDQy6+sZChUifo/gq4i
14
- R+ewpr1h56glmyDwsOqfPRiKYoM/HfTQvUY/P1UcXcni4+10G8y1zrlo5q2C5m1o
15
- wsbtwGMh5mC2HkejcUxzIDMi7FklSQoOKf/WbI1cicw1FPN6IzCWygu83BnwuNyn
16
- n7qdGFJf1lxdEy+cLAwPf82CFQKBgQDfB4oYUNaBY/86XAKd6RQdShh9kUPIi5gC
17
- SvNT5iZoD2yOARaXM6N9NRL8ecjUgCOwFE0EXhwbxLA7jhEp0tlUEcLTSG82m/K/
18
- X7iQSYrAUHMeYDnDqw4ldbvOiwyW4BTyQXZt/zBNsoeRXwymTGW9QcGkJ8dlSH7r
19
- GJofyzwkNwKBgBZx3ueUOLWlw23yWU+8FQ7rTa5pnay1OVxGWGJXyfyOTHeLe9a4
20
- kgWYhEixWFN4lv0FRANDPrElIv/QT3CUh5flN4LcZgcNyDusoUA69ORLLeYRLPCK
21
- 2UKulp+c/IO0RdwaTk3XWF2r92nmJWorhH8Ar1I4E+O5fUO3hHBiF7BlAoGAUcJ4
22
- zGsuB6b51JIQX4UnW4ljKv0KxUTvR7/kLdiMMc+dsv0RJALJJ0hCmo2mosgKzSzU
23
- Awrg8QkMtfhhsFxcUWZlboD6TCGlPqS/ix16oP+dBACwjFtzxyW+4Hu29MRWyWxY
24
- FwDa21DcQ919TKVrEpM9gokWnrmR1WtibM9bT8cCgYAuuMnTGwe7IdJE8yX1Z8cj
25
- +uhL1zhkRanLDB2VCRjIaVAhYFlsKSCLkdoPtlhm+DCpa9dIYn/5Lr675kXT75Om
26
- NlzA8al4hspE4SmtzWkH8rPRlBNlSx6MXMIpx2B5WAv+vt+pF35f3QDyCrFHlYG4
27
- 0EosZqYcRWBQUmNETOPTWQ==
2
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrmhAfEgcfLpAN
3
+ tHUf8p7wKXzP86KDB74LM4XW+yVjXl1rbNOzxOxhI2xSXhHkX1yPwiimP6DIB5Pr
4
+ 7/yNDBVexIcPsh29mMGxhiBWyPZRW0XZ1QnsgwhhELi/6T2feVpk1nhZ33vhG1XX
5
+ 0a9abWoIdjtiFVUJ6JTxfxQilSgdRrrPd+y7+8WLFCNd2kPkLgbsRxqH3D46siG6
6
+ uCiQxwmHVxjDaTAfp9dxiDkdzP3BqF5vHjPD3Vx/Xo9Lp/D+ACgcrzksMAU1X+ew
7
+ 1uEN8iVDIBotYsChccZcNeJgDdaJNURLst77jFG/KwQSHIcnS2mmwPuh5vKA7mXr
8
+ 26WQVQvJAgMBAAECggEABw/aKmiBJUaoPD4mefalFTxZ2Ut9GcAYRrOmO4Uz93wf
9
+ I1V+prBma27MIt128PNpNrZ+b9gLaiFLXxDDZOZqnI8BLBN6ue5G81hcwpbhhyfW
10
+ ztqqSRgmi0BnSqji5bTBFEhcun3HW58BaCuGOEDA4IwFZdlmTw1J+uBtpVOG1kyH
11
+ wksa8jG9Bt1opgMB8R0E+ujPv8MMRmA/H7VG+h3ms6eoRdfMK0F+TVSCF8C+Le0Y
12
+ UZ6wr8LMNsxYFFlP0SCNewsWO2zfo3M2wMPJp7ZVGF2yHZXLxjPNGNHXiw7w/Ycs
13
+ yOOhJjPkiApBKf+7cTfzwBycs6NNTtjCL2zbCU4TkQKBgQDjvdqVbMsHeAW2NVCv
14
+ k6f+Q2FrVzqL4NtbJKncXYAFeSzt8VsZyqjKv6xJVAgmu8iUunISrIEVd3OvN5wn
15
+ 1TB7TEcNOW58kS++4zCS6kExUSA+8NpgSn9pXbZ40rN/IXbUSY1/9k2eCUVHT6fe
16
+ uV+vLFFIFutypC9Q6oDNDLxdeQKBgQDA5PHIwaANqHPryJxw4vCEFa6cx9Y4CRWx
17
+ 7wotxecnN6lcg7NmKmDOd+/mknNm7XQ4vYjZhe0yd8snAwW7F++b5MO58xoWQtrg
18
+ Fe4upGb+bERNcdB18xvUh2l6s87vyNSFHSOFQ9T92z6bAPYwKfwa2LAdEaLi0iED
19
+ 9TSQw4+c0QKBgBfqPePnEsLsBZ2Gp7xoQwHeTYKaHFnH+QDvLq8nWmQ4v1EVQT3K
20
+ HEN+MTdaDakTmxKTKZtwZfW9H3bM3icIqhiof+WRnFqUXHEtJlKnKsVlAX/ahvQJ
21
+ nquP3bN+XL8C4pmUR7miQbBQxOeiI7yttrCMdNSfI4ZcW2Oh3Hir17PxAoGBAJjN
22
+ xGtQV0X6h7nkdWlHnVhFBMnV1HEXYURPJDUWbQK4KUXMXrU0JPPqNvTkVt7WhBbe
23
+ riYC3d/43cnNryCC2aEZG6OWXLf9xf114fS4743knkbrPkPxjb5SdtBo2ClDYncA
24
+ mj7beUKQB4wqutt0pu1tcjXU3r/nzZehCz8SbBXBAoGBAMl40ZQiBJGxQlE7iWlP
25
+ yrgZKzQEZTPF49TI3Fz3hxLKaSfdQL9xnSClh6EJ7CXyhv1ZjSMCup4EteUIEi7L
26
+ aPX6zDGXiFagH+gcgFxWilG4JwkfrQuK7In146CGjSDuOttwtls0FhG1ghOdEvpf
27
+ KpZmyVGab0l9DzNFJdCd0TK/
28
28
  -----END PRIVATE KEY-----
@@ -1,19 +1,19 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIDCTCCAfGgAwIBAgIUGsxsjjaY2GmUHIEyJG5gYWxgDvcwDQYJKoZIhvcNAQEL
3
- BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMDgwNzE0MDk1OFoXDTI0MDgw
4
- NjE0MDk1OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5
- AAOCAQ8AMIIBCgKCAQEAteeUOpqv35iN+xfapxopMukwNMyzgEFf5NHUG8dL8/ft
6
- 93Tu4fh548rumFDldWLxczloPNZ3wlK093HKDlEJmoWp8qbr1uXKraKVx9G90LH2
7
- rvsIQOTzOqM0d3FeB68UGtNIS3uxhQ2Vp6AybK/UKWdG1k5gZttJGfUYODXoFEG/
8
- S1fSqNBzGwuM3Z0Rm9h53ZTcZxAndahQi3bj0kUaMnxIurDmiwKQUa9oG8iot9wC
9
- hpxKR9jfDZZdXWFo4wubbs+ti8zUs0/HVjfXfOR0LMadPorB67LNNvnQhf4AbJ4a
10
- +MknJoCGOPVxZaT3aiskhEz29EwSsdlwPhIpf13mgwIDAQABo1MwUTAdBgNVHQ4E
11
- FgQU5X2k15HyvorxnwSoZdgQgncfLtYwHwYDVR0jBBgwFoAU5X2k15HyvorxnwSo
12
- ZdgQgncfLtYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAOypg
13
- 7ioP4P6R/8gomTzzo+PrFO9YeM4ZyJrwLRTSlWTudjfmMRwIMKmhWo/tFo+e5+IZ
14
- uYaXYWCtyQSz6fb/XF1RlbmeBd4NhT81mITh9h8s7Hjece1UX73Gc5n2uZr7sokl
15
- 9D+rcXPPKhrCvNAuRIEcQSKpueKcADizT/KFDjqX5DhztiiZmk62Ki/K+aTWSl0q
16
- Pj3s0KNBCL8sUecvjGoaxNamJ3v22tYmKPi5G79suat5kvEjkndU+F0Y/EM5lwsH
17
- whChIjCR4RBU01pPG6S2zzvgNUrVHc7W7CsSX55U641oHd9r5gLkBQ0t9Sa5xXqd
18
- saWvwAR8yKA6G+t5PQ==
2
+ MIIDCTCCAfGgAwIBAgIUVyYTM/kwgl4YG+Mo6bsxRO2GhZIwDQYJKoZIhvcNAQEL
3
+ BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMDgyMjEwMTM1NFoXDTI0MDgy
4
+ MTEwMTM1NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5
+ AAOCAQ8AMIIBCgKCAQEAq5oQHxIHHy6QDbR1H/Ke8Cl8z/Oigwe+CzOF1vslY15d
6
+ a2zTs8TsYSNsUl4R5F9cj8Iopj+gyAeT6+/8jQwVXsSHD7IdvZjBsYYgVsj2UVtF
7
+ 2dUJ7IMIYRC4v+k9n3laZNZ4Wd974RtV19GvWm1qCHY7YhVVCeiU8X8UIpUoHUa6
8
+ z3fsu/vFixQjXdpD5C4G7Ecah9w+OrIhurgokMcJh1cYw2kwH6fXcYg5Hcz9wahe
9
+ bx4zw91cf16PS6fw/gAoHK85LDAFNV/nsNbhDfIlQyAaLWLAoXHGXDXiYA3WiTVE
10
+ S7Le+4xRvysEEhyHJ0tppsD7oebygO5l69ulkFULyQIDAQABo1MwUTAdBgNVHQ4E
11
+ FgQUSpFymv93oCm2Yr4jgjuqitOhgRIwHwYDVR0jBBgwFoAUSpFymv93oCm2Yr4j
12
+ gjuqitOhgRIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEALsY1
13
+ NufIewwelamaUb/50cbPMKEQkTpOYnMP//DFWcw1yGzcPNuB4sPKgLhhX1ggknIr
14
+ RWaj+6Oow4KVKcPDdG6r5P5IEl6hn+432KM4Gu9BQasg6seReO4uf9chV9sdV06z
15
+ d4jgXIFxIw/1PVMEPn9qj079HNMJuRcpyxGwbzeeRhu3WZpO4qlIx2/MpucARtSU
16
+ d1f0/IOo6A/yASrn9Y5fZ2rOxj3mk4Yu0z1+WpsUVLV/ES/mxgIIRwlxp9z6F5r2
17
+ tpEyJxM+XoUOn4mTe6xIgfM8DVS910p6Yne+z0xSnXZpVU8sEg0RtZH6kfSCc9Vp
18
+ JwtG3xq1/oB8KNDAXw==
19
19
  -----END CERTIFICATE-----
@@ -28,7 +28,7 @@ import { exec as nodeExec } from 'child_process';
28
28
  import fs from 'fs-extra';
29
29
  import os from 'os';
30
30
  import path from 'path';
31
- import crypto from "crypto";
31
+ import crypto, { webcrypto } from "crypto";
32
32
  import { getRootPath, readJsonFromRoot } from './utils.js';
33
33
  import ConsoleWriter from './ConsoleWriter.js';
34
34
  const certSafePeriod = 1000 * 60 * 60 * 24; // 24 hours
@@ -140,7 +140,7 @@ export async function createCertFile(config, open) {
140
140
  }
141
141
  break;
142
142
  }
143
- passphrase = crypto.getRandomValues(new Uint32Array(1))[0].toString().substring(2);
143
+ passphrase = (crypto.getRandomValues || webcrypto.getRandomValues)(new Uint32Array(1))[0].toString().substring(2);
144
144
  config.server.passphrase = passphrase;
145
145
  fs.writeFileSync(path.join(rootPath, confPath), JSON.stringify(config));
146
146
  // for windows 8 / 8.1 / server 2012 R2 /
@@ -11,11 +11,14 @@ export default class CommandManager {
11
11
  minifyJS: false,
12
12
  minify: false,
13
13
  devServerPort: options.port,
14
- stats: options.stats
14
+ stats: options.stats,
15
+ skipApiCheck: options.skipApi,
16
+ allLocales: options.allLocales
15
17
  };
16
18
  const visualManager = new VisualManager(rootPath);
17
19
  await visualManager
18
20
  .prepareVisual()
21
+ .validateVisual()
19
22
  .initializeWebpack(webpackOptions);
20
23
  visualManager.startWebpackServer(options.drop);
21
24
  }
@@ -31,10 +34,13 @@ export default class CommandManager {
31
34
  minifyJS: options.minify,
32
35
  minify: options.minify,
33
36
  compression: options.compression,
34
- stats: options.stats
37
+ stats: options.stats,
38
+ skipApiCheck: options.skipApi,
39
+ allLocales: options.allLocales
35
40
  };
36
41
  new VisualManager(rootPath)
37
42
  .prepareVisual()
43
+ .validateVisual()
38
44
  .initializeWebpack(webpackOptions)
39
45
  .then(visualManager => visualManager.generatePackage());
40
46
  }
@@ -0,0 +1,43 @@
1
+ import { Severity } from "./features/FeatureTypes.js";
2
+ import * as features from "./features/index.js";
3
+ export var Status;
4
+ (function (Status) {
5
+ Status[Status["Success"] = 0] = "Success";
6
+ Status[Status["Error"] = 1] = "Error";
7
+ })(Status || (Status = {}));
8
+ export class FeatureManager {
9
+ features = Object.keys(features).map(key => features[key]);
10
+ validate(stage, sourceInstance) {
11
+ const result = {
12
+ status: Status.Success,
13
+ logs: {
14
+ errors: [],
15
+ warnings: [],
16
+ info: [],
17
+ deprecation: []
18
+ }
19
+ };
20
+ this.features
21
+ .filter(feature => feature.stage == stage)
22
+ .filter(feature => feature.visualFeatureType & sourceInstance.visualFeatureType)
23
+ .filter(feature => !feature.isSupported(sourceInstance))
24
+ .forEach(({ errorMessage, severity }) => {
25
+ switch (severity) {
26
+ case Severity.Error:
27
+ result.status = Status.Error;
28
+ result.logs.errors.push(errorMessage);
29
+ break;
30
+ case Severity.Warning:
31
+ result.logs.warnings.push(errorMessage);
32
+ break;
33
+ case Severity.Info:
34
+ result.logs.info.push(errorMessage);
35
+ break;
36
+ case Severity.Deprecation:
37
+ result.logs.deprecation.push(errorMessage);
38
+ break;
39
+ }
40
+ });
41
+ return result;
42
+ }
43
+ }
package/lib/Package.js ADDED
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Power BI Visual CLI
3
+ *
4
+ * Copyright (c) Microsoft Corporation
5
+ * All rights reserved.
6
+ * MIT License
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the ""Software""), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+ "use strict";
27
+ import isMatch from "lodash.ismatch";
28
+ /**
29
+ * Represents an instance of a visual package based on file path
30
+ */
31
+ export default class Package {
32
+ sourceCode;
33
+ capabilities;
34
+ visualFeatureType;
35
+ constructor(sourceCode, capabilities, visualFeatureType) {
36
+ this.sourceCode = sourceCode;
37
+ this.capabilities = capabilities;
38
+ this.visualFeatureType = visualFeatureType;
39
+ }
40
+ contain(keyword) {
41
+ return this.sourceCode.includes(keyword);
42
+ }
43
+ isCapabilityEnabled(expectedObject) {
44
+ return isMatch(this.capabilities, expectedObject);
45
+ }
46
+ }
package/lib/Visual.js ADDED
@@ -0,0 +1,34 @@
1
+ import { compareVersions } from "compare-versions";
2
+ import { VisualFeatureType } from "./features/FeatureTypes.js";
3
+ export class Visual {
4
+ visualFeatureType;
5
+ capabilities;
6
+ config;
7
+ packageJSON;
8
+ visualVersion;
9
+ constructor(capabilities, config, packageJson) {
10
+ this.capabilities = capabilities;
11
+ this.config = config;
12
+ this.visualFeatureType = this.getVisualFeatureType();
13
+ this.packageJSON = packageJson;
14
+ this.visualVersion = config.visual.version;
15
+ }
16
+ doesAPIVersionMatch(minAPIversion) {
17
+ return compareVersions(this.config.apiVersion ?? minAPIversion, minAPIversion) !== -1;
18
+ }
19
+ doesESLlintSupported() {
20
+ return Object.entries(this.packageJSON.scripts).some(([, value]) => value.includes("eslint"));
21
+ }
22
+ isVisualVersionValid(length) {
23
+ return this.visualVersion.split(".").length === length;
24
+ }
25
+ getVisualFeatureType() {
26
+ const isMatrixSupported = this.capabilities?.dataViewMappings?.some(dataView => dataView.matrix);
27
+ const isSlicer = Boolean(this.capabilities?.objects?.general?.properties?.filter?.type?.filter);
28
+ let type = isSlicer ? VisualFeatureType.Slicer : VisualFeatureType.NonSlicer;
29
+ if (isMatrixSupported) {
30
+ type = type | VisualFeatureType.Matrix;
31
+ }
32
+ return type;
33
+ }
34
+ }
@@ -33,7 +33,12 @@ import ConsoleWriter from './ConsoleWriter.js';
33
33
  import VisualGenerator from './VisualGenerator.js';
34
34
  import { readJsonFromRoot, readJsonFromVisual } from './utils.js';
35
35
  import WebpackWrap from './WebPackWrap.js';
36
+ import Package from './Package.js';
37
+ import { Visual } from "./Visual.js";
38
+ import { FeatureManager, Status } from "./FeatureManager.js";
39
+ import { Severity, Stage } from "./features/FeatureTypes.js";
36
40
  import TemplateFetcher from "./TemplateFetcher.js";
41
+ const globalConfig = readJsonFromRoot('config.json');
37
42
  const PBIVIZ_FILE = 'pbiviz.json';
38
43
  /**
39
44
  * Represents an instance of a visual package based on file path
@@ -41,7 +46,11 @@ const PBIVIZ_FILE = 'pbiviz.json';
41
46
  export default class VisualManager {
42
47
  basePath;
43
48
  pbivizConfig;
49
+ capabilities;
44
50
  webpackConfig;
51
+ visual;
52
+ package;
53
+ featureManager;
45
54
  compiler;
46
55
  webpackDevServer;
47
56
  constructor(rootPath) {
@@ -50,12 +59,19 @@ export default class VisualManager {
50
59
  prepareVisual() {
51
60
  if (this.doesPBIVIZExists()) {
52
61
  this.pbivizConfig = readJsonFromVisual(PBIVIZ_FILE, this.basePath);
62
+ this.createVisualInstance();
53
63
  }
54
64
  else {
55
- throw new Error(PBIVIZ_FILE + ' not found. You must be in the root of a visual project to run this command.');
65
+ ConsoleWriter.error(PBIVIZ_FILE + ' not found. You must be in the root of a visual project to run this command.');
66
+ process.exit(1);
56
67
  }
57
68
  return this;
58
69
  }
70
+ createVisualInstance() {
71
+ this.capabilities = readJsonFromVisual("capabilities.json", this.basePath);
72
+ const packageJSON = readJsonFromVisual("package.json", this.basePath);
73
+ this.visual = new Visual(this.capabilities, this.pbivizConfig, packageJSON);
74
+ }
59
75
  async initializeWebpack(webpackOptions) {
60
76
  const webpackWrap = new WebpackWrap();
61
77
  this.webpackConfig = await webpackWrap.generateWebpackConfig(this, webpackOptions);
@@ -64,6 +80,9 @@ export default class VisualManager {
64
80
  }
65
81
  generatePackage() {
66
82
  const callback = (err, stats) => {
83
+ this.createPackageInstance();
84
+ const logs = this.validatePackage();
85
+ this.outputResults(logs);
67
86
  this.parseCompilationResults(err, stats);
68
87
  };
69
88
  this.compiler.run(callback);
@@ -95,6 +114,32 @@ export default class VisualManager {
95
114
  process.exit(1);
96
115
  }
97
116
  }
117
+ validateVisual() {
118
+ this.featureManager = new FeatureManager();
119
+ const { status, logs } = this.featureManager.validate(Stage.PreBuild, this.visual);
120
+ this.outputResults(logs);
121
+ if (status === Status.Error) {
122
+ process.exit(1);
123
+ }
124
+ return this;
125
+ }
126
+ validatePackage() {
127
+ const featureManager = new FeatureManager();
128
+ const { logs } = featureManager.validate(Stage.PostBuild, this.package);
129
+ return logs;
130
+ }
131
+ outputResults({ errors, deprecation, warnings, info }) {
132
+ const featuresTotalLog = {
133
+ errors: `Visual doesn't support some features required for all custom visuals:`,
134
+ deprecation: `Some features are going to be required soon, please update the visual:`,
135
+ warn: `Visual doesn't support some features recommended for all custom visuals:`,
136
+ info: `Visual can be improved by adding some features:`
137
+ };
138
+ this.outputLogsWithHeadMessage(featuresTotalLog.errors, errors, Severity.Error);
139
+ this.outputLogsWithHeadMessage(featuresTotalLog.deprecation, deprecation, Severity.Deprecation);
140
+ this.outputLogsWithHeadMessage(featuresTotalLog.warn, warnings, Severity.Warning);
141
+ this.outputLogsWithHeadMessage(featuresTotalLog.info, info, Severity.Info);
142
+ }
98
143
  displayInfo() {
99
144
  if (this.pbivizConfig) {
100
145
  ConsoleWriter.infoTable(this.pbivizConfig);
@@ -177,6 +222,11 @@ export default class VisualManager {
177
222
  this.webpackDevServer = null;
178
223
  }
179
224
  }
225
+ createPackageInstance() {
226
+ const pathToJSContent = path.join((this.pbivizConfig.build ?? globalConfig.build).dropFolder, "visual.js");
227
+ const sourceCode = fs.readFileSync(pathToJSContent, "utf8");
228
+ this.package = new Package(sourceCode, this.capabilities, this.visual.visualFeatureType);
229
+ }
180
230
  parseCompilationResults(err, stats) {
181
231
  ConsoleWriter.blank();
182
232
  if (err) {
@@ -190,4 +240,27 @@ export default class VisualManager {
190
240
  ConsoleWriter.done('Build completed successfully');
191
241
  }
192
242
  }
243
+ outputLogsWithHeadMessage(headMessage, logs, severity) {
244
+ if (!logs.length) {
245
+ return;
246
+ }
247
+ let outputLog;
248
+ switch (severity) {
249
+ case Severity.Error || Severity.Deprecation:
250
+ outputLog = ConsoleWriter.error;
251
+ break;
252
+ case Severity.Warning:
253
+ outputLog = ConsoleWriter.warning;
254
+ break;
255
+ default:
256
+ outputLog = ConsoleWriter.info;
257
+ break;
258
+ }
259
+ if (headMessage) {
260
+ outputLog(headMessage);
261
+ ConsoleWriter.blank();
262
+ }
263
+ logs.forEach(error => outputLog(error));
264
+ ConsoleWriter.blank();
265
+ }
193
266
  }
@@ -9,7 +9,7 @@ import { exec as processExec } from 'child_process';
9
9
  import lodashCloneDeep from 'lodash.clonedeep';
10
10
  import ExtraWatchWebpackPlugin from 'extra-watch-webpack-plugin';
11
11
  import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
12
- import { PowerBICustomVisualsWebpackPlugin } from 'powerbi-visuals-webpack-plugin';
12
+ import { PowerBICustomVisualsWebpackPlugin, LocalizationLoader } from 'powerbi-visuals-webpack-plugin';
13
13
  import ConsoleWriter from './ConsoleWriter.js';
14
14
  import { resolveCertificate } from "./CertificateTools.js";
15
15
  import { readJsonFromRoot, readJsonFromVisual } from './utils.js';
@@ -33,12 +33,13 @@ export default class WebPackWrap {
33
33
  await fs.createFile(visualPluginFile);
34
34
  }
35
35
  static loadAPIPackage() {
36
- try {
37
- return import("file://" + path.join(process.cwd(), "node_modules", "powerbi-visuals-api", "index.js"));
38
- }
39
- catch (ex) {
40
- return null;
36
+ const apiPath = path.join(process.cwd(), "node_modules", "powerbi-visuals-api");
37
+ const doesAPIExist = fs.pathExistsSync(apiPath);
38
+ if (!doesAPIExist) {
39
+ ConsoleWriter.error(`Can't find powerbi-visuals-api package`);
40
+ process.exit(1);
41
41
  }
42
+ return import("file://" + path.join(apiPath, "index.js"));
42
43
  }
43
44
  async installAPIpackage() {
44
45
  const apiVersion = this.pbiviz.apiVersion ? `~${this.pbiviz.apiVersion}` : "latest";
@@ -115,45 +116,50 @@ export default class WebPackWrap {
115
116
  return env;
116
117
  }
117
118
  async configureCustomVisualsWebpackPlugin(visualPackage, options, tsconfig) {
118
- const pluginConfiguration = lodashCloneDeep(visualPackage.pbivizConfig);
119
- //(?=\D*$) - positive look-ahead to find last version symbols and exclude any non-digit symbols after the version.
120
- const regexFullVersion = /(?<=powerbi-visuals-api@)((?:\d+\.?){1,3})/g;
121
- const regexMinorVersion = /\d+(?:\.\d+)?/;
122
- let apiVersionInstalled;
123
- try {
124
- const subprocess = await exec('npm list powerbi-visuals-api version');
125
- apiVersionInstalled = subprocess.stdout.match(regexFullVersion)[0];
126
- }
127
- catch (err) {
128
- ConsoleWriter.warning(`"powerbi-visuals-api" is not installed`);
119
+ if (options.skipApiCheck) {
120
+ ConsoleWriter.warning(`Skipping API check. Tools started with --skipApi flag.`);
129
121
  }
130
- // if the powerbi-visual-api package wasn't installed
131
- // install the powerbi-visual-api, with version from apiVersion in pbiviz.json
132
- // or the latest API, if apiVersion is absent in pbiviz.json
133
- if (!apiVersionInstalled || !this.pbiviz.apiVersion || this.pbiviz.apiVersion.match(regexMinorVersion)[0] != apiVersionInstalled.match(regexMinorVersion)[0]) {
134
- ConsoleWriter.warning(`installed "powerbi-visuals-api" version - "${apiVersionInstalled}", is not match with the version specified in pbviz.json - "${this.pbiviz.apiVersion}".`);
135
- await this.installAPIpackage();
122
+ else {
123
+ await this.configureAPIVersion();
136
124
  }
137
- // pluginConfiguration.env = await this.getEnvironmentDetails();
138
125
  const api = await WebPackWrap.loadAPIPackage();
139
- pluginConfiguration.apiVersion = api.version;
140
- pluginConfiguration.capabilitiesSchema = api.schemas.capabilities;
141
- pluginConfiguration.pbivizSchema = api.schemas.pbiviz;
142
- pluginConfiguration.stringResourcesSchema = api.schemas.stringResources;
143
- pluginConfiguration.dependenciesSchema = api.schemas.dependencies;
144
- pluginConfiguration.customVisualID = `CustomVisual_${this.pbiviz.visual.guid}`.replace(/[^\w\s]/gi, '');
145
- pluginConfiguration.devMode = (typeof options.devMode === "undefined") ? true : options.devMode;
146
- pluginConfiguration.generatePbiviz = options.generatePbiviz;
147
- pluginConfiguration.generateResources = options.generateResources;
148
- pluginConfiguration.minifyJS = options.minifyJS;
149
126
  const dependenciesPath = this.pbiviz.dependencies && path.join(process.cwd(), this.pbiviz.dependencies);
150
- pluginConfiguration.dependencies = fs.existsSync(dependenciesPath) ? this.pbiviz.dependencies : null;
151
- pluginConfiguration.modules = typeof tsconfig.compilerOptions.outDir !== "undefined";
152
- pluginConfiguration.visualSourceLocation = path.posix.relative(config.build.precompileFolder, tsconfig.files[0]).replace(/(\.ts)x|\.ts/, "");
153
- pluginConfiguration.pluginLocation = path.join(config.build.precompileFolder, "visualPlugin.ts");
154
- pluginConfiguration.compression = options.compression;
127
+ let pluginConfiguration = {
128
+ ...lodashCloneDeep(visualPackage.pbivizConfig),
129
+ apiVersion: api.version,
130
+ capabilitiesSchema: api.schemas.capabilities,
131
+ pbivizSchema: api.schemas.pbiviz,
132
+ stringResourcesSchema: api.schemas.stringResources,
133
+ dependenciesSchema: api.schemas.dependencies,
134
+ customVisualID: `CustomVisual_${this.pbiviz.visual.guid}`.replace(/[^\w\s]/gi, ''),
135
+ devMode: options.devMode,
136
+ generatePbiviz: options.generatePbiviz,
137
+ generateResources: options.generateResources,
138
+ minifyJS: options.minifyJS,
139
+ dependencies: fs.existsSync(dependenciesPath) ? this.pbiviz.dependencies : null,
140
+ modules: typeof tsconfig.compilerOptions.outDir !== "undefined",
141
+ visualSourceLocation: path.posix.relative(config.build.precompileFolder, tsconfig.files[0]).replace(/(\.ts)x|\.ts/, ""),
142
+ pluginLocation: path.join(config.build.precompileFolder, "visualPlugin.ts"),
143
+ compression: options.compression
144
+ };
155
145
  return pluginConfiguration;
156
146
  }
147
+ async configureAPIVersion() {
148
+ //(?<=powerbi-visuals-api@) - positive look-behind to find version installed in visual and get 3 level version.
149
+ const regexFullVersion = /(?<=powerbi-visuals-api@)((?:\d+\.?){1,3})/g;
150
+ //get only first 2 parts of version
151
+ const regexMajorVersion = /\d+(?:\.\d+)?/;
152
+ const listResults = (await exec('npm list powerbi-visuals-api version')).stdout;
153
+ const installedAPIVersion = listResults.match(regexFullVersion)[0] ?? "not found";
154
+ const doesAPIExist = fs.pathExistsSync(path.join(process.cwd(), "node_modules", "powerbi-visuals-api"));
155
+ // if the powerbi-visual-api package wasn't installed install the powerbi-visual-api,
156
+ // with version from apiVersion in pbiviz.json or the latest API, if apiVersion is absent in pbiviz.json
157
+ const isAPIConfigured = doesAPIExist && installedAPIVersion && this.pbiviz.apiVersion;
158
+ if (!isAPIConfigured || this.pbiviz.apiVersion.match(regexMajorVersion)[0] != installedAPIVersion.match(regexMajorVersion)[0]) {
159
+ ConsoleWriter.warning(`installed "powerbi-visuals-api" version - "${installedAPIVersion}", is not match with the version specified in pbviz.json - "${this.pbiviz.apiVersion}".`);
160
+ await this.installAPIpackage();
161
+ }
162
+ }
157
163
  async appendPlugins(options, visualPackage, tsconfig) {
158
164
  const pluginConfiguration = await this.configureCustomVisualsWebpackPlugin(visualPackage, options, tsconfig);
159
165
  let statsFilename = config.build.stats.split("/").pop();
@@ -177,23 +183,27 @@ export default class WebPackWrap {
177
183
  }));
178
184
  }
179
185
  }
180
- async useLoader({ fast = false }) {
181
- let tsOptions = {};
182
- if (fast) {
183
- tsOptions = {
184
- transpileOnly: false,
185
- experimentalWatchApi: false
186
- };
187
- }
186
+ async configureLoaders({ fast = false, includeAllLocales = false }) {
188
187
  this.webpackConfig.module.rules.push({
189
188
  test: /(\.ts)x?$/,
190
189
  use: [
191
190
  {
192
191
  loader: "ts-loader",
193
- options: tsOptions
192
+ options: fast
193
+ ? {
194
+ transpileOnly: false,
195
+ experimentalWatchApi: false
196
+ }
197
+ : {}
194
198
  }
195
199
  ]
196
200
  });
201
+ if (!includeAllLocales) {
202
+ this.webpackConfig.module.rules.push({
203
+ test: /powerbiGlobalizeLocales\.js$/,
204
+ loader: LocalizationLoader
205
+ });
206
+ }
197
207
  }
198
208
  async prepareWebPackConfig(visualPackage, options, tsconfig) {
199
209
  this.webpackConfig = Object.assign({}, await import('./webpack.config.js')).default;
@@ -206,8 +216,9 @@ export default class WebPackWrap {
206
216
  await this.appendPlugins(options, visualPackage, tsconfig);
207
217
  await this.configureDevServer(visualPackage, options.devServerPort);
208
218
  await this.configureVisualPlugin(options, tsconfig, visualPackage);
209
- await this.useLoader({
210
- fast: options.fast
219
+ await this.configureLoaders({
220
+ fast: options.fast,
221
+ includeAllLocales: options.allLocales
211
222
  });
212
223
  return this.webpackConfig;
213
224
  }
@@ -228,7 +239,9 @@ export default class WebPackWrap {
228
239
  devServerPort: 8080,
229
240
  fast: false,
230
241
  compression: 0,
231
- stats: true
242
+ stats: true,
243
+ skipApiCheck: false,
244
+ allLocales: false
232
245
  }) {
233
246
  const tsconfig = readJsonFromVisual('tsconfig.json');
234
247
  this.pbiviz = readJsonFromVisual('pbiviz.json');
@@ -0,0 +1,16 @@
1
+ import { readJsonFromRoot } from "../utils.js";
2
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
3
+ export default class APIVersion {
4
+ static featureName = "Api";
5
+ static severity = Severity.Error;
6
+ static stage = Stage.PreBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer;
8
+ static minAPIversion;
9
+ static errorMessage;
10
+ static isSupported(visual) {
11
+ const globalConfig = readJsonFromRoot('config.json');
12
+ this.minAPIversion = globalConfig.constants.minAPIversion;
13
+ this.errorMessage = `API version must be at least ${this.minAPIversion}.`;
14
+ return visual.doesAPIVersionMatch(this.minAPIversion);
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class AdvancedEditMode {
3
+ static featureName = "Advanced Edit Mode";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/advanced-edit-mode";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return !packageInstance.isCapabilityEnabled({ advancedEditMode: 0 }); // 0 - Advanced edit mode is disabled
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class AllowInteractions {
3
+ static featureName = "Allow Interactions";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/visuals-interactions";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain('.allowInteractions');
10
+ }
11
+ }
@@ -0,0 +1,16 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class AnalyticsPane {
3
+ static featureName = "Analytics Pane";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/analytics-pane";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return (packageInstance.isCapabilityEnabled({
10
+ objects: {
11
+ objectCategory: 2
12
+ }
13
+ }) ||
14
+ packageInstance.contain("analyticsPane=true"));
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ export default class BaseFeature {
2
+ static severity;
3
+ static stage;
4
+ static visualFeatureType;
5
+ static featureName;
6
+ static documentationLink;
7
+ static get errorMessage() {
8
+ return `${this.featureName} - ${this.documentationLink}`;
9
+ }
10
+ static isSupported() { }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class Bookmarks {
3
+ static featureName = "Bookmarks";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/bookmarks-support";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain("applySelectionFromFilter") || packageInstance.contain("registerOnSelectCallback");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class ColorPalette {
3
+ static featureName = "Color Palette";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/add-colors-power-bi-visual";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".colorPalette");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class ConditionalFormatting {
3
+ static featureName = "Conditional Formatting";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/conditional-format";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".createDataViewWildcardSelector");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class ContextMenu {
3
+ static featureName = "Context Menu";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/context-menu";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".showContextMenu");
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class DrillDown {
3
+ static featureName = "Drill Down";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/drill-down-support";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({
10
+ drilldown: {
11
+ roles: []
12
+ }
13
+ });
14
+ }
15
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class ESLint {
3
+ static featureName = "ESLint";
4
+ static documentationLink = "https://github.com/microsoft/eslint-plugin-powerbi-visuals";
5
+ static severity = Severity.Error;
6
+ static stage = Stage.PreBuild;
7
+ static visualFeatureType = VisualFeatureType.All;
8
+ static isSupported(visual) {
9
+ return visual.doesESLlintSupported();
10
+ }
11
+ }
@@ -0,0 +1,23 @@
1
+ export var Severity;
2
+ (function (Severity) {
3
+ Severity["Error"] = "error";
4
+ Severity["Deprecation"] = "deprecation";
5
+ Severity["Warning"] = "warning";
6
+ Severity["Info"] = "info";
7
+ })(Severity || (Severity = {}));
8
+ export var Stage;
9
+ (function (Stage) {
10
+ Stage["PreBuild"] = "pre-build";
11
+ Stage["PostBuild"] = "post-build";
12
+ })(Stage || (Stage = {}));
13
+ export var VisualFeatureType;
14
+ (function (VisualFeatureType) {
15
+ VisualFeatureType[VisualFeatureType["NonSlicer"] = 2] = "NonSlicer";
16
+ VisualFeatureType[VisualFeatureType["Slicer"] = 4] = "Slicer";
17
+ VisualFeatureType[VisualFeatureType["Matrix"] = 8] = "Matrix";
18
+ VisualFeatureType[VisualFeatureType["All"] = 14] = "All";
19
+ })(VisualFeatureType || (VisualFeatureType = {}));
20
+ // Interaction types: Selection or filter (slicer)
21
+ // Slicer type: Basic, Advanced, Tuple filter, Identity filter
22
+ // Visual Type: TS/JS or R-Visual or RHTML
23
+ // Dataview Type: Single or Matrix or Table or Category or All
@@ -0,0 +1,21 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class FetchMoreData {
3
+ static featureName = "Fetch More Data";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/fetch-more-data";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({
10
+ dataViewMappings: [
11
+ {
12
+ table: {
13
+ rows: {
14
+ dataReductionAlgorithm: {}
15
+ }
16
+ }
17
+ }
18
+ ]
19
+ });
20
+ }
21
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class FileDownload {
3
+ static featureName = "File Download";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/file-download-api";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".downloadService") && packageInstance.contain(".exportVisualsContent");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class FormatPane {
3
+ static featureName = "Format Pane";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/format-pane";
5
+ static severity = Severity.Deprecation;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.All;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain("getFormattingModel");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class HighContrast {
3
+ static featureName = "High Contrast";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/high-contrast-support";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".isHighContrast");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class HighlightData {
3
+ static featureName = "Highlight Data";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/highlight";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ supportsHighlight: true });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class KeyboardNavigation {
3
+ static featureName = "Keyboard Navigation";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/supportskeyboardfocus-feature";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ supportsKeyboardFocus: true });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class LandingPage {
3
+ static featureName = "Landing Page";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/landing-page";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ supportsLandingPage: true });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class LaunchURL {
3
+ static featureName = "Launch URL";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/launch-url";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".launchUrl");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class LocalStorage {
3
+ static featureName = "Local Storage";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/local-storage";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".storageService");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class Localizations {
3
+ static featureName = "Localizations";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/localization";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".createLocalizationManager");
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class ModalDialog {
3
+ static featureName = "Modal Dialog";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/create-display-dialog-box";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".openModalDialog");
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class RenderingEvents {
3
+ static featureName = "Rendering Events";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/event-service";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.All;
8
+ static isSupported(packageInstance) {
9
+ const keywords = [".eventService", ".renderingStarted", ".renderingFinished"];
10
+ return !keywords.some(keyword => !packageInstance.contain(keyword));
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class SelectionAcrossVisuals {
3
+ static featureName = "Selection Across Visuals";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/supportsmultivisualselection-feature";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ supportsMultiVisualSelection: true });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class SyncSlicer {
3
+ static featureName = "Sync Slicer";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/enable-sync-slicers";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ supportsSynchronizingFilterState: true });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class Tooltips {
3
+ static featureName = "Tooltips";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/add-tooltips";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain("tooltipService") && packageInstance.isCapabilityEnabled({ tooltips: {} });
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class TotalSubTotal {
3
+ static featureName = "Total SubTotal";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/total-subtotal-api";
5
+ static severity = Severity.Warning;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.Matrix | VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.isCapabilityEnabled({ subtotals: true });
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class VisualVersion {
3
+ static featureName = "Visual version";
4
+ static expectedVersionLength = 4;
5
+ static errorMessage = `${this.featureName} should consist of ${this.expectedVersionLength} parts. Update the pbiviz.json file`;
6
+ static severity = Severity.Error;
7
+ static stage = Stage.PreBuild;
8
+ static visualFeatureType = VisualFeatureType.All;
9
+ static isSupported(visual) {
10
+ return visual.isVisualVersionValid(this.expectedVersionLength);
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ import { Severity, Stage, VisualFeatureType } from "./FeatureTypes.js";
2
+ export default class WarningIcon {
3
+ static featureName = "Warning Icon";
4
+ static documentationLink = "https://learn.microsoft.com/en-us/power-bi/developer/visuals/visual-display-warning-icon";
5
+ static severity = Severity.Info;
6
+ static stage = Stage.PostBuild;
7
+ static visualFeatureType = VisualFeatureType.NonSlicer | VisualFeatureType.Slicer;
8
+ static isSupported(packageInstance) {
9
+ return packageInstance.contain(".displayWarningIcon");
10
+ }
11
+ }
@@ -0,0 +1,29 @@
1
+ import AdvancedEditMode from './AdvancedEditMode.js';
2
+ import AllowInteractions from './AllowInteractions.js';
3
+ import AnalyticsPane from './AnalyticsPane.js';
4
+ import Bookmarks from './Bookmarks.js';
5
+ import ColorPalette from './ColorPalette.js';
6
+ import ConditionalFormatting from './ConditionalFormatting.js';
7
+ import ContextMenu from './ContextMenu.js';
8
+ import DrillDown from './DrillDown.js';
9
+ import FetchMoreData from './FetchMoreData.js';
10
+ import FileDownload from './FileDownload.js';
11
+ import FormatPane from './FormatPane.js';
12
+ import HighContrast from './HighContrast.js';
13
+ import HighlightData from './HighlightData.js';
14
+ import KeyboardNavigation from './KeyboardNavigation.js';
15
+ import LandingPage from './LandingPage.js';
16
+ import LaunchURL from './LaunchURL.js';
17
+ import Localizations from './Localizations.js';
18
+ import LocalStorage from './LocalStorage.js';
19
+ import ModalDialog from './ModalDialog.js';
20
+ import RenderingEvents from './RenderingEvents.js';
21
+ import SelectionAcrossVisuals from './SelectionAcrossVisuals.js';
22
+ import SyncSlicer from './SyncSlicer.js';
23
+ import Tooltips from './Tooltips.js';
24
+ import TotalSubTotal from './TotalSubTotal.js';
25
+ import WarningIcon from './WarningIcon.js';
26
+ import APIVersion from './APIVersion.js';
27
+ import ESLint from './ESLint.js';
28
+ import VisualVersion from './VisualVersion.js';
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, ESLint, VisualVersion };
@@ -1,5 +1,4 @@
1
1
  import { getRootPath, readJsonFromRoot } from './utils.js';
2
- import { LocalizationLoader } from "powerbi-visuals-webpack-plugin";
3
2
  import MiniCssExtractPlugin from "mini-css-extract-plugin";
4
3
  import TerserPlugin from "terser-webpack-plugin";
5
4
  import path from "path";
@@ -62,10 +61,6 @@ const webpackConfig = {
62
61
  {
63
62
  test: /\.(woff|ttf|ico|woff2|jpg|jpeg|png|webp|gif|svg|eot)$/i,
64
63
  type: 'asset/inline'
65
- },
66
- {
67
- test: /powerbiGlobalizeLocales\.js$/,
68
- loader: LocalizationLoader
69
64
  }
70
65
  ]
71
66
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powerbi-visuals-tools",
3
- "version": "5.0.3",
3
+ "version": "5.2.0-beta.1",
4
4
  "description": "Command line tool for creating and publishing visuals for Power BI",
5
5
  "main": "./bin/pbiviz.js",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "buffer": "^6.0.3",
37
37
  "chalk": "^5.3.0",
38
38
  "commander": "^11.0.0",
39
- "compare-versions": "^6.0.0",
39
+ "compare-versions": "^6.1.0",
40
40
  "console-browserify": "^1.2.0",
41
41
  "constants-browserify": "^1.0.0",
42
42
  "crypto-browserify": "^3.12.0",
@@ -49,11 +49,12 @@
49
49
  "inline-source-map": "^0.6.2",
50
50
  "json-loader": "0.5.7",
51
51
  "jszip": "^3.10.1",
52
- "less": "^4.1.3",
52
+ "less": "^4.2.0",
53
53
  "less-loader": "^11.1.3",
54
54
  "lodash.clonedeep": "4.5.0",
55
55
  "lodash.defaults": "4.2.0",
56
56
  "lodash.isequal": "4.5.0",
57
+ "lodash.ismatch": "^4.4.0",
57
58
  "mini-css-extract-plugin": "^2.7.6",
58
59
  "os-browserify": "^0.3.0",
59
60
  "path-browserify": "^1.0.1",
@@ -79,9 +80,9 @@
79
80
  },
80
81
  "devDependencies": {
81
82
  "@typescript-eslint/eslint-plugin": "^5.62.0",
82
- "eslint": "^8.45.0",
83
+ "eslint": "^8.47.0",
83
84
  "eslint-plugin-powerbi-visuals": "^0.8.1",
84
- "jasmine": "5.0.2",
85
+ "jasmine": "5.1.0",
85
86
  "jasmine-spec-reporter": "7.0.0",
86
87
  "semver": "7.5.4",
87
88
  "tree-kill": "1.2.2",