powerbi-visuals-tools 5.4.2 → 5.5.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 CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  This page contains information about changes to the PowerBI Visual Tools (pbiviz).
4
4
 
5
+ ## 5.5.0
6
+ * Changed path for storing certificates. It allows the certificate to be reused regardless of tools version. New path is `({home directory}/pbiviz-certs)`.
7
+ * Windows version lower 10 is deprecated.
8
+ * Resolve of symlinks is disabled.
9
+
10
+ ## 5.4.3
11
+ * Fixed bug with missing plugins for Eslint.
12
+ * New flag `--use-default` for `pbiviz package` and `pbiviz lint` commands. Use this command to lint files according to recommended lint config.
13
+
5
14
  ## 5.4.2
6
15
  * Added the **node: false** option to the webpack plugin to eliminate the use of the potentially dangerous **new Function()** method, which ensures compatibility with the Node.js runtime.
7
16
  * Introduced support for *.mjs ECMAScript modules.
package/bin/pbiviz.js CHANGED
@@ -72,6 +72,7 @@ pbiviz
72
72
  pbiviz
73
73
  .command('lint')
74
74
  .option('--fix', 'Enable autofixing of lint errors')
75
+ .option('--use-default', 'Use recommended eslintrc file')
75
76
  .action(options => {
76
77
  CommandManager.lint({ ...options, verbose: true }, rootPath);
77
78
  });
@@ -100,6 +101,7 @@ pbiviz
100
101
  .option('-l, --all-locales', "Keeps all locale files in the package. By default only used inside stringResources folder locales are included.")
101
102
  .option('-v, --verbose', "Enables verbose logging")
102
103
  .option('--fix', 'Enable autofixing of lint errors')
104
+ .option('--use-default', 'Use recommended eslintrc file')
103
105
  .option('-p, --pbiviz-file <pbiviz-file>', "Path to pbiviz.json file (useful for debugging)", pbivizFile)
104
106
  .addOption(new Option('-c, --compression <compressionLevel>', "Enables compression of visual package")
105
107
  .choices(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])
package/config.json CHANGED
@@ -17,9 +17,11 @@
17
17
  "package": { "dropFolder": "dist" },
18
18
  "server": {
19
19
  "assetsRoute": "/assets",
20
- "privateKey": "certs/PowerBICustomVisualTest_private.key",
21
- "certificate": "certs/PowerBICustomVisualTest_public.crt",
22
- "pfx": "certs/PowerBICustomVisualTest_public.pfx"
20
+ "certificateFolder": "pbiviz-certs",
21
+ "privateKey": "PowerBICustomVisualTest_private.key",
22
+ "certificate": "PowerBICustomVisualTest_public.crt",
23
+ "pfx": "PowerBICustomVisualTest_public.pfx",
24
+ "passphrase": "PowerBICustomVisualTestPass.txt"
23
25
  },
24
26
  "visualTemplates": {
25
27
  "circlecard": "https://codeload.github.com/microsoft/powerbi-visuals-circlecard-react/zip/master"
@@ -30,285 +30,181 @@ import fs from 'fs-extra';
30
30
  import os from 'os';
31
31
  import path from 'path';
32
32
  import crypto from "crypto";
33
- import { getRootPath, readJsonFromRoot } from './utils.js';
33
+ import { readJsonFromRoot } from './utils.js';
34
34
  import ConsoleWriter from './ConsoleWriter.js';
35
35
  const certSafePeriod = 1000 * 60 * 60 * 24; // 24 hours
36
- const rootPath = getRootPath();
37
- const confPath = '/config.json';
38
- function exec(command, callback) {
36
+ const config = readJsonFromRoot('config.json');
37
+ const pathToCertFolder = path.join(os.homedir(), config.server.certificateFolder);
38
+ const secretFilesPath = {
39
+ certPath: path.join(pathToCertFolder, config.server.certificate),
40
+ keyPath: path.join(pathToCertFolder, config.server.privateKey),
41
+ pfxPath: path.join(pathToCertFolder, config.server.pfx),
42
+ passphrasePath: path.join(pathToCertFolder, config.server.passphrase)
43
+ };
44
+ function exec(command, options, callback) {
39
45
  return new Promise((resolve, reject) => {
40
- nodeExec(command, callback ? callback : (err, stdout, stderr) => {
46
+ const defaultCallback = (err, stdout, stderr) => {
41
47
  if (err) {
42
48
  reject(stderr);
43
49
  }
44
50
  resolve(stdout);
45
- });
51
+ };
52
+ nodeExec(command, options, callback ? callback : defaultCallback);
46
53
  });
47
54
  }
48
55
  export async function createCertificate() {
49
- const config = readJsonFromRoot('config.json');
50
- const certPath = await getCertFile(config, true);
56
+ const certPath = await getCertFile(true);
51
57
  if (!certPath) {
52
58
  ConsoleWriter.error("Certificate not found. The new certificate will be generated");
53
- await createCertFile(config, true);
59
+ await createCertFile(true);
54
60
  }
55
61
  else {
56
- await openCertFile(config);
62
+ await openCertFile();
57
63
  }
58
64
  }
59
- export async function createCertFile(config, open) {
65
+ export async function createCertFile(open = false) {
60
66
  ConsoleWriter.info(`Generating a new certificate...`);
61
67
  const subject = "localhost";
62
68
  const keyLength = 2048;
63
- const algorithm = "sha256";
64
69
  const validPeriod = 365;
65
- if (typeof open === 'undefined') {
66
- open = false;
67
- }
68
- const certPath = path.join(rootPath, config.server.certificate);
69
- const keyPath = path.join(rootPath, config.server.privateKey);
70
- const pfxPath = path.join(rootPath, config.server.pfx);
70
+ const { certPath, keyPath, pfxPath } = secretFilesPath;
71
71
  const openCmds = {
72
72
  linux: 'openssl',
73
73
  darwin: 'openssl',
74
- win32: 'powershell'
74
+ win32: 'pwsh'
75
75
  };
76
76
  let startCmd = openCmds[os.platform()];
77
- if (startCmd) {
78
- try {
79
- let createCertCommand = "";
80
- switch (os.platform()) {
81
- case "linux":
82
- await removeCertFiles(certPath, keyPath);
83
- createCertCommand =
84
- ` req -newkey rsa:${keyLength}` +
85
- ` -nodes` +
86
- ` -keyout ${keyPath}` +
87
- ` -x509 ` +
88
- ` -days ${validPeriod} ` +
89
- ` -out ${certPath} ` +
90
- ` -subj "/CN=${subject}"`;
91
- await exec(`${startCmd} ${createCertCommand}`);
92
- if (await fs.exists(certPath)) {
93
- ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
94
- if (open) {
95
- await openCertFile(config);
96
- }
97
- }
98
- break;
99
- case "darwin":
100
- await removeCertFiles(certPath, keyPath);
101
- createCertCommand =
102
- ` req -newkey rsa:${keyLength}` +
103
- ` -nodes` +
104
- ` -keyout ${keyPath}` +
105
- ` -x509 ` +
106
- ` -days ${validPeriod} ` +
107
- ` -out ${certPath} ` +
108
- ` -subj "/CN=${subject}"`;
109
- await exec(`${startCmd} ${createCertCommand}`);
110
- if (await fs.exists(certPath)) {
111
- ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
112
- if (open) {
113
- await openCertFile(config);
114
- }
115
- }
116
- break;
117
- case "win32":
118
- // for windows 7 and others
119
- // 6.1 - Windows 7
120
- const osVersion = os.release().split(".");
121
- if ((Number(osVersion[0]) === 6 && Number(osVersion[1]) === 1) || Number(osVersion[0]) < 6) {
122
- await removeCertFiles(certPath, keyPath, pfxPath);
123
- startCmd = "openssl";
124
- createCertCommand =
125
- ` req -newkey rsa:${keyLength}` +
126
- ` -nodes` +
127
- ` -keyout ${keyPath}` +
128
- ` -x509 ` +
129
- ` -days ${validPeriod} ` +
130
- ` -out ${certPath} ` +
131
- ` -subj "/CN=${subject}"`;
132
- await exec(`${startCmd} ${createCertCommand}`);
133
- if (await fs.exists(certPath)) {
134
- ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
135
- if (open) {
136
- await openCertFile(config);
137
- }
138
- }
139
- break;
140
- }
141
- let randomValues;
142
- if (crypto.getRandomValues !== undefined) {
143
- randomValues = crypto.getRandomValues(new Uint32Array(1));
144
- }
145
- else {
146
- randomValues = crypto.webcrypto.getRandomValues(new Uint32Array(1));
147
- }
148
- const passphrase = randomValues[0].toString().substring(2);
149
- config.server.passphrase = passphrase;
150
- fs.writeFileSync(path.join(rootPath, confPath), JSON.stringify(config));
151
- // for windows 8 / 8.1 / server 2012 R2 /
152
- if (Number(osVersion[0]) === 6 && (Number(osVersion[1]) === 2 || Number(osVersion[1]) === 3)) {
153
- // for 10
154
- createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
155
- ` New-SelfSignedCertificate ` +
156
- ` -DnsName localhost ` +
157
- ` -CertStoreLocation Cert:\\CurrentUser\\My ` +
158
- ` | select Thumbprint | ` +
159
- ` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
160
- ` Export-PfxCertificate -Cert $cert` +
161
- ` -FilePath '${pfxPath}' ` +
162
- ` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
163
- await exec(`${startCmd} "${createCertCommand}"`);
164
- }
165
- else {
166
- // for window 10 / server 2016
167
- createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
168
- ` New-SelfSignedCertificate ` +
169
- ` -DnsName localhost ` +
170
- ` -HashAlgorithm ${algorithm} ` +
171
- ` -Type Custom ` +
172
- ` -Subject ${subject} ` +
173
- ` -KeyAlgorithm RSA ` +
174
- ` -KeyLength ${keyLength} ` +
175
- ` -KeyExportPolicy Exportable ` +
176
- ` -CertStoreLocation Cert:\\CurrentUser\\My ` +
177
- ` -NotAfter (get-date).AddDays(${validPeriod}) ` +
178
- ` | select Thumbprint | ` +
179
- ` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
180
- ` Export-PfxCertificate -Cert $cert` +
181
- ` -FilePath '${pfxPath}' ` +
182
- ` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
183
- await exec(`${startCmd} "${createCertCommand}"`);
184
- }
185
- if (await fs.exists(pfxPath)) {
186
- ConsoleWriter.info(`Certificate generated. Location is ${pfxPath}. Passphrase is '${passphrase}'`);
77
+ if (!startCmd) {
78
+ ConsoleWriter.error(['Unknown platform. Please place a custom-generated certificate in:', certPath]);
79
+ return;
80
+ }
81
+ try {
82
+ let createCertCommand = "";
83
+ let passphrase = "";
84
+ fs.ensureDirSync(path.dirname(certPath));
85
+ switch (os.platform()) {
86
+ case "linux":
87
+ case "darwin":
88
+ createCertCommand =
89
+ ` req -newkey rsa:${keyLength}` +
90
+ ` -nodes` +
91
+ ` -keyout ${keyPath}` +
92
+ ` -x509 ` +
93
+ ` -days ${validPeriod} ` +
94
+ ` -out ${certPath} ` +
95
+ ` -subj "/CN=${subject}"`;
96
+ await Promise.all([
97
+ removeCertFiles(certPath, keyPath),
98
+ exec(`${startCmd} ${createCertCommand}`)
99
+ ]);
100
+ if (await fs.exists(certPath)) {
101
+ ConsoleWriter.info(`Certificate generated. Location is ${certPath}`);
102
+ if (open) {
103
+ await openCertFile();
187
104
  }
188
- break;
189
- default:
190
- ConsoleWriter.error('Unknown platform');
191
- }
192
- }
193
- catch (e) {
194
- if (e && e.message && e.message.indexOf("'openssl' is not recognized as an internal or external command") > 0) {
195
- ConsoleWriter.warning('Create certificate error:');
196
- ConsoleWriter.warning('OpenSSL is not installed or not available from command line');
197
- ConsoleWriter.info('Install OpenSSL from https://www.openssl.org or https://wiki.openssl.org/index.php/Binaries');
198
- ConsoleWriter.info('and try again');
199
- ConsoleWriter.info('Read more at');
200
- ConsoleWriter.info('https://github.com/Microsoft/PowerBI-visuals/blob/master/tools/CreateCertificate.md#manual');
201
- }
202
- else {
203
- ConsoleWriter.error(['Create certificate error:', e]);
204
- }
105
+ }
106
+ break;
107
+ case "win32":
108
+ passphrase = getRandomValues()[0].toString().substring(2);
109
+ createCertCommand = `$cert = ('Cert:\\CurrentUser\\My\\' + (` +
110
+ ` New-SelfSignedCertificate ` +
111
+ ` -DnsName localhost ` +
112
+ ` -Type Custom ` +
113
+ ` -Subject 'CN=${subject}' ` +
114
+ ` -KeyAlgorithm RSA ` +
115
+ ` -KeyLength ${keyLength} ` +
116
+ ` -KeyExportPolicy Exportable ` +
117
+ ` -CertStoreLocation Cert:\\CurrentUser\\My ` +
118
+ ` -NotAfter (get-date).AddDays(${validPeriod}) ` +
119
+ ` | select Thumbprint | ` +
120
+ ` ForEach-Object { $_.Thumbprint.ToString() }).toString()); ` +
121
+ ` Export-PfxCertificate -Cert $cert` +
122
+ ` -FilePath '${pfxPath}' ` +
123
+ ` -Password (ConvertTo-SecureString -String '${passphrase}' -Force -AsPlainText)`;
124
+ await Promise.all([
125
+ removeCertFiles(certPath, keyPath, pfxPath),
126
+ exec(`${startCmd} -Command "${createCertCommand}"`),
127
+ savePassphrase(passphrase)
128
+ ]);
129
+ if (await fs.exists(pfxPath)) {
130
+ ConsoleWriter.info(`Certificate generated. Location is ${pfxPath}. Passphrase is ${passphrase}`);
131
+ }
132
+ break;
133
+ default:
134
+ ConsoleWriter.error('Unknown platform');
135
+ break;
205
136
  }
206
137
  }
207
- else {
208
- ConsoleWriter.error(['Unknown platform. Please place a custom-generated certificate in:', certPath]);
138
+ catch (e) {
139
+ if (e && e.message && e.message.indexOf("'openssl' is not recognized as an internal or external command") > 0) {
140
+ ConsoleWriter.warning('Create certificate error:');
141
+ ConsoleWriter.warning('OpenSSL is not installed or not available from command line');
142
+ ConsoleWriter.info('Install OpenSSL from https://www.openssl.org or https://wiki.openssl.org/index.php/Binaries');
143
+ ConsoleWriter.info('and try again');
144
+ ConsoleWriter.info('Read more at');
145
+ ConsoleWriter.info('https://github.com/Microsoft/PowerBI-visuals/blob/master/tools/CreateCertificate.md#manual');
146
+ }
147
+ else {
148
+ ConsoleWriter.error(['Create certificate error:', e]);
149
+ }
209
150
  }
210
151
  }
211
- async function getCertFile(config, silent) {
212
- if (typeof silent === "undefined") {
213
- silent = false;
214
- }
215
- const cert = path.join(rootPath, config.server.certificate);
216
- const pfx = path.join(rootPath, config.server.pfx);
217
- if (await fs.exists(cert)) {
218
- return cert;
152
+ async function getCertFile(silent = false) {
153
+ const { certPath, pfxPath, passphrasePath } = secretFilesPath;
154
+ if (await fs.exists(certPath)) {
155
+ return certPath;
219
156
  }
220
- if (await fs.exists(pfx)) {
221
- if (config.server.passphrase) {
222
- if (!silent) {
223
- ConsoleWriter.info(`Use '${config.server.passphrase}' passphrase to install PFX certificate.`);
224
- }
157
+ if (await fs.exists(pfxPath)) {
158
+ if (!silent && await fs.exists(passphrasePath)) {
159
+ const passphrase = await fs.readFile(passphrasePath, 'utf8');
160
+ ConsoleWriter.info(`Use '${passphrase}' passphrase to install PFX certificate.`);
225
161
  }
226
- return pfx;
162
+ return pfxPath;
227
163
  }
228
164
  if (!silent) {
229
165
  ConsoleWriter.info('Certificate not found. Call `pbiviz install-cert` command to create the new certificate');
230
166
  }
231
167
  return null;
232
168
  }
233
- async function openCertFile(config) {
234
- const certPath = await getCertFile(config);
235
- if (!certPath) {
236
- return null;
237
- }
169
+ async function openCertFile() {
238
170
  const openCmds = {
239
171
  linux: 'xdg-open',
240
172
  darwin: 'open',
241
173
  win32: 'powershell start'
242
174
  };
243
175
  const startCmd = openCmds[os.platform()];
244
- if (startCmd) {
245
- try {
246
- await exec(`${startCmd} "${certPath}"`);
247
- }
248
- catch (e) {
249
- ConsoleWriter.info(['Certificate path:', certPath]);
176
+ const certPath = await getCertFile();
177
+ try {
178
+ if (!startCmd || !certPath) {
179
+ throw new Error();
250
180
  }
181
+ await exec(`${startCmd} "${certPath}"`);
251
182
  }
252
- else {
183
+ catch (e) {
253
184
  ConsoleWriter.info(['Certificate path:', certPath]);
254
185
  }
255
186
  }
256
- export async function removeCertFiles(certPath, keyPath, pfxPath) {
257
- try {
258
- await fs.unlink(certPath);
259
- }
260
- catch (e) {
261
- if (!e.message.indexOf("no such file or directory")) {
262
- throw e;
263
- }
264
- }
265
- try {
266
- await fs.unlink(keyPath);
267
- }
268
- catch (e) {
269
- if (!e.message.indexOf("no such file or directory")) {
270
- throw e;
187
+ export async function removeCertFiles(...paths) {
188
+ paths.forEach(async (path) => {
189
+ try {
190
+ await fs.unlink(path);
271
191
  }
272
- }
273
- try {
274
- await fs.unlink(pfxPath);
275
- }
276
- catch (e) {
277
- if (!e.message.indexOf("no such file or directory")) {
278
- throw e;
192
+ catch (e) {
193
+ if (!e.message.indexOf("no such file or directory")) {
194
+ throw e;
195
+ }
279
196
  }
280
- }
281
- }
282
- async function getGlobalPbivizCerts(config) {
283
- const options = {};
284
- try {
285
- const location = (await exec('npm ls -g powerbi-visuals-tools')).split("\n")[0];
286
- const certPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.certificate);
287
- const keyPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.privateKey);
288
- const pfxPath = path.join(location, "node_modules", "powerbi-visuals-tools", config.server.pfx);
289
- const globalPbivizConfig = path.join(location, "node_modules", "powerbi-visuals-tools", "config.json");
290
- options.passphrase = fs.existsSync(globalPbivizConfig) && fs.readJSONSync(globalPbivizConfig).server.passphrase;
291
- const CertFileVerified = await verifyCertFile(keyPath, certPath, pfxPath, options.passphrase);
292
- options.cert = fs.existsSync(certPath) && certPath;
293
- options.key = fs.existsSync(keyPath) && keyPath;
294
- options.pfx = fs.existsSync(pfxPath) && CertFileVerified && pfxPath;
295
- }
296
- catch (err) {
297
- ConsoleWriter.warning(`Global certificate error: ${err}`);
298
- }
299
- if (!options.cert && !options.pfx) {
300
- ConsoleWriter.warning(`Global instance of valid pbiviz certificate not found.`);
301
- }
302
- return options;
197
+ });
303
198
  }
304
199
  export async function resolveCertificate() {
305
- const config = readJsonFromRoot('config.json');
306
200
  const options = {};
307
- const keyPath = path.join(rootPath, config.server.privateKey);
308
- const certPath = path.join(rootPath, config.server.certificate);
309
- const pfxPath = path.join(rootPath, config.server.pfx);
310
- if (config.server.passphrase) {
311
- options.passphrase = config.server.passphrase;
201
+ const { certPath, keyPath, pfxPath, passphrasePath } = secretFilesPath;
202
+ const isCertificateValid = await verifyCertFile(keyPath, certPath, pfxPath, passphrasePath);
203
+ if (!isCertificateValid) {
204
+ await createCertFile();
205
+ }
206
+ if (await fs.exists(passphrasePath)) {
207
+ options.passphrase = await fs.readFile(passphrasePath, 'utf8');
312
208
  }
313
209
  if (await fs.exists(keyPath)) {
314
210
  options.key = await fs.readFile(keyPath);
@@ -319,72 +215,24 @@ export async function resolveCertificate() {
319
215
  if (await fs.exists(pfxPath)) {
320
216
  options.pfx = await fs.readFile(pfxPath);
321
217
  }
322
- const CertFileVerified = await verifyCertFile(keyPath, certPath, pfxPath, options.passphrase);
323
- if ((!options.cert && !options.pfx) || !CertFileVerified) {
324
- ConsoleWriter.warning("Local valid certificate not found.");
325
- ConsoleWriter.info("Checking global instance of pbiviz certificate...");
326
- const globalPbivizOptions = await getGlobalPbivizCerts(config);
327
- if (!globalPbivizOptions.cert && !globalPbivizOptions.pfx) {
328
- await createCertFile(config, true);
329
- if (!(await getCertFile(config, true))) {
330
- ConsoleWriter.error('Certificate wasn\'t created');
331
- throw new Error("Call `pbiviz install-cert` command to create the new certificate");
332
- }
333
- else {
334
- if (config.server.passphrase) {
335
- options.passphrase = config.server.passphrase;
336
- }
337
- if (await fs.exists(keyPath)) {
338
- options.key = await fs.readFile(keyPath);
339
- }
340
- if (await fs.exists(certPath)) {
341
- options.cert = await fs.readFile(certPath);
342
- }
343
- if (await fs.exists(pfxPath)) {
344
- options.pfx = await fs.readFile(pfxPath);
345
- }
346
- }
347
- }
348
- else {
349
- // copy certs to local instance
350
- ConsoleWriter.info("Copy server certificate from global instance of pbiviz...");
351
- if (globalPbivizOptions.cert) {
352
- await fs.copyFile(globalPbivizOptions.cert, path.join(rootPath, config.server.certificate));
353
- options.certificate = config.server.certificate;
354
- }
355
- if (globalPbivizOptions.key) {
356
- await fs.copyFile(globalPbivizOptions.key, path.join(rootPath, config.server.privateKey));
357
- options.privateKey = config.server.privateKey;
358
- }
359
- if (globalPbivizOptions.pfx) {
360
- await fs.copyFile(globalPbivizOptions.pfx, path.join(rootPath, config.server.pfx));
361
- // need to pass whole file instead path to file
362
- options.pfx = await fs.readFile(path.join(rootPath, config.server.pfx));
363
- options.passphrase = globalPbivizOptions.passphrase;
364
- // eslint-disable-next-line require-atomic-updates
365
- config.server.passphrase = globalPbivizOptions.passphrase;
366
- }
367
- await fs.writeFile(path.join(rootPath, confPath), JSON.stringify(config));
368
- }
369
- }
370
218
  return options;
371
219
  }
372
- export async function verifyCertFile(keyPath, certPath, pfxPath, passphrase) {
373
- let verifyCertDate;
220
+ export async function verifyCertFile(keyPath, certPath, pfxPath, passphrasePath) {
221
+ let verifyCertDate = false;
374
222
  try {
375
223
  let endDateStr;
376
- // For Windows OS:
377
224
  if (os.platform() === "win32") {
378
- if (!fs.existsSync(pfxPath) || !passphrase) {
379
- return false;
225
+ if (!fs.existsSync(pfxPath) || !fs.existsSync(passphrasePath)) {
226
+ throw new Error('PFX or passphrase file not found');
380
227
  }
381
- const certStr = await exec(`powershell.exe (New-Object System.Security.Cryptography.X509Certificates.X509Certificate2('${pfxPath}','${passphrase}')).NotAfter.ToString('yyyy-MM-dd HH:mm:ss')`);
228
+ const passphrase = await fs.readFile(passphrasePath, 'utf8');
229
+ const command = `(New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("${pfxPath}",'${passphrase}')).NotAfter.ToString('yyyy-MM-dd HH:mm:ss')`;
230
+ const certStr = await exec(command, { shell: 'powershell.exe' });
382
231
  endDateStr = certStr.trim();
383
232
  }
384
- // For Linux and Mac/darwin OS:
385
233
  else if (os.platform() === "linux" || os.platform() === "darwin") {
386
- if (!fs.existsSync(certPath)) {
387
- return false;
234
+ if (!(await fs.exists(certPath))) {
235
+ throw new Error('Certificate file not found');
388
236
  }
389
237
  endDateStr = await exec(`openssl x509 -enddate -noout -in ${certPath} | cut -d = -f 2`);
390
238
  }
@@ -394,13 +242,22 @@ export async function verifyCertFile(keyPath, certPath, pfxPath, passphrase) {
394
242
  ConsoleWriter.info(`Certificate is valid.`);
395
243
  }
396
244
  else {
397
- ConsoleWriter.warning(`Certificate is invalid!`);
398
- removeCertFiles(certPath, keyPath, pfxPath);
245
+ throw new Error('Certificate is invalid');
399
246
  }
400
247
  }
401
248
  catch (err) {
402
249
  ConsoleWriter.warning(`Certificate verification error: ${err}`);
403
- removeCertFiles(certPath, keyPath, pfxPath);
250
+ removeCertFiles(certPath, keyPath, pfxPath, passphrasePath);
404
251
  }
405
252
  return verifyCertDate;
406
253
  }
254
+ const getRandomValues = () => {
255
+ if (crypto.getRandomValues !== undefined) {
256
+ return crypto.getRandomValues(new Uint32Array(1));
257
+ }
258
+ return crypto.webcrypto.getRandomValues(new Uint32Array(1));
259
+ };
260
+ const savePassphrase = async (passphrase) => {
261
+ const { passphrasePath } = secretFilesPath;
262
+ await fs.writeFileSync(passphrasePath, passphrase);
263
+ };
@@ -49,6 +49,7 @@ export default class CommandManager {
49
49
  const lintOptions = {
50
50
  verbose: options.verbose,
51
51
  fix: options.fix,
52
+ useDefault: options.useDefault
52
53
  };
53
54
  const visual = new VisualManager(rootPath).prepareVisual(options.pbivizFile);
54
55
  await visual.runLintValidation(lintOptions);
@@ -1,37 +1,44 @@
1
1
  import { ESLint } from "eslint";
2
+ import path from 'path';
3
+ import fs from 'fs-extra';
2
4
  import ConsoleWriter from "./ConsoleWriter.js";
3
- import { fileExists, getRootPath } from "./utils.js";
5
+ import { getRootPath } from "./utils.js";
4
6
  export class LintValidator {
5
7
  visualPath;
6
8
  rootPath;
9
+ isVerboseMode;
10
+ useDefault;
11
+ shouldFix;
7
12
  config;
8
- defaultConfig;
9
13
  linterInstance;
10
- constructor(fix = false) {
14
+ constructor({ verbose, fix, useDefault }) {
11
15
  this.visualPath = process.cwd();
12
16
  this.rootPath = getRootPath();
13
- this.prepareConfig(fix);
17
+ this.isVerboseMode = verbose;
18
+ this.useDefault = useDefault;
19
+ this.shouldFix = fix;
20
+ this.prepareConfig();
14
21
  this.linterInstance = new ESLint(this.config);
15
22
  }
16
23
  /**
17
24
  * Runs lint validation in the visual folder
18
25
  */
19
- async runLintValidation({ verbose, fix }) {
26
+ async runLintValidation() {
20
27
  ConsoleWriter.info("Running lint check...");
21
28
  // By default it will lint all files in the src of current working directory, but some files can be excluded in .eslintignore
22
- const results = await this.linterInstance.lintFiles("src/**/*");
23
- if (fix) {
29
+ const results = await this.linterInstance.lintFiles("src/");
30
+ if (this.shouldFix) {
24
31
  await this.fixErrors(results);
25
32
  }
26
- await this.outputResults(results, verbose);
33
+ await this.outputResults(results);
27
34
  ConsoleWriter.info("Lint check completed.");
28
35
  }
29
36
  async fixErrors(results) {
30
37
  ConsoleWriter.info("Lint fixing errors...");
31
38
  await ESLint.outputFixes(results);
32
39
  }
33
- async outputResults(results, verbose) {
34
- if (verbose) {
40
+ async outputResults(results) {
41
+ if (this.isVerboseMode) {
35
42
  const formatter = await this.linterInstance.loadFormatter("stylish");
36
43
  const formattedResults = await formatter.format(results);
37
44
  console.log(formattedResults);
@@ -46,33 +53,42 @@ export class LintValidator {
46
53
  }
47
54
  }
48
55
  }
49
- prepareConfig(fix) {
50
- this.defaultConfig = {
51
- overrideConfig: {
52
- env: {
53
- browser: true,
54
- es6: true,
55
- es2022: true
56
- },
57
- plugins: [
58
- "powerbi-visuals"
59
- ],
60
- extends: [
61
- "plugin:powerbi-visuals/recommended"
62
- ]
63
- },
64
- extensions: [".ts", ".tsx"],
65
- resolvePluginsRelativeTo: this.rootPath,
66
- useEslintrc: false,
67
- fix
56
+ prepareConfig() {
57
+ const requiredConfig = {
58
+ extensions: [".js", ".jsx", ".ts", ".tsx"],
59
+ fix: this.shouldFix,
60
+ resolvePluginsRelativeTo: this.getPluginPath()
68
61
  };
69
- const eslintrcExtensions = ['.json', '.js', '.ts'];
70
- if (eslintrcExtensions.some(el => fileExists(this.visualPath, `.eslintrc${el}`))) {
71
- this.config = { fix };
62
+ const eslintrcExtensions = ['.json', '.js', '.cjs', '.ts', ''];
63
+ if (!this.useDefault && eslintrcExtensions.some(el => fs.existsSync(path.join(this.visualPath, `.eslintrc${el}`)))) {
64
+ this.config = requiredConfig;
72
65
  }
73
66
  else {
74
- ConsoleWriter.warning("No .eslintrc file found in the visual folder. Using default config.");
75
- this.config = this.defaultConfig;
67
+ ConsoleWriter.warning("Using recommended eslint config.");
68
+ this.config = {
69
+ ...requiredConfig,
70
+ overrideConfig: {
71
+ env: {
72
+ browser: true,
73
+ es6: true,
74
+ es2022: true
75
+ },
76
+ plugins: [
77
+ "powerbi-visuals"
78
+ ],
79
+ extends: [
80
+ "plugin:powerbi-visuals/recommended"
81
+ ]
82
+ },
83
+ useEslintrc: false,
84
+ };
76
85
  }
77
86
  }
87
+ getPluginPath() {
88
+ const pluginPaths = [
89
+ path.resolve(this.visualPath, "node_modules", "eslint-plugin-powerbi-visuals"),
90
+ path.resolve(this.rootPath, "node_modules", "eslint-plugin-powerbi-visuals")
91
+ ];
92
+ return pluginPaths.find(fs.existsSync);
93
+ }
78
94
  }
@@ -68,9 +68,17 @@ export default class VisualManager {
68
68
  }
69
69
  return this;
70
70
  }
71
- runLintValidation(options) {
72
- const linter = new LintValidator(options.fix);
73
- linter.runLintValidation(options);
71
+ async runLintValidation(options) {
72
+ try {
73
+ const linter = new LintValidator(options);
74
+ await linter.runLintValidation();
75
+ }
76
+ catch (error) {
77
+ ConsoleWriter.error("Can't run lint validation.");
78
+ if (options.verbose) {
79
+ ConsoleWriter.error(error.message);
80
+ }
81
+ }
74
82
  }
75
83
  createVisualInstance() {
76
84
  this.capabilities = readJsonFromVisual("capabilities.json", this.basePath);
package/lib/utils.js CHANGED
@@ -22,7 +22,7 @@ export function getRootPath() {
22
22
  const pathToDirectory = fileURLToPath(import.meta.url);
23
23
  return path.join(pathToDirectory, "..", "..");
24
24
  }
25
- export function readFileFromRoot(filePath) {
25
+ function readFileFromRoot(filePath) {
26
26
  return fs.readFileSync(path.join(getRootPath(), filePath), "utf8");
27
27
  }
28
28
  export function readJsonFromRoot(filePath) {
@@ -31,6 +31,3 @@ export function readJsonFromRoot(filePath) {
31
31
  export function readJsonFromVisual(filePath, visualPath) {
32
32
  return JSON.parse(fs.readFileSync(path.join(visualPath ?? process.cwd(), filePath), "utf8"));
33
33
  }
34
- export function fileExists(pathToFile, fileName) {
35
- return fs.existsSync(path.join(pathToFile, fileName));
36
- }
@@ -72,6 +72,7 @@ const webpackConfig = {
72
72
  "powerbi-visuals-api": 'null'
73
73
  },
74
74
  resolve: {
75
+ symlinks: false,
75
76
  extensions: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.css'],
76
77
  modules: ['node_modules', path.resolve(rootPath, 'node_modules')],
77
78
  fallback: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powerbi-visuals-tools",
3
- "version": "5.4.2",
3
+ "version": "5.5.0",
4
4
  "description": "Command line tool for creating and publishing visuals for Power BI",
5
5
  "main": "./bin/pbiviz.js",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "homepage": "https://github.com/Microsoft/PowerBI-visuals-tools#readme",
31
31
  "dependencies": {
32
- "@typescript-eslint/parser": "^6.17.0",
32
+ "@typescript-eslint/parser": "^7.12.0",
33
33
  "assert": "^2.1.0",
34
34
  "async": "^3.2.5",
35
35
  "browserify-zlib": "^0.2.0",
@@ -40,22 +40,22 @@
40
40
  "console-browserify": "^1.2.0",
41
41
  "constants-browserify": "^1.0.0",
42
42
  "crypto-browserify": "^3.12.0",
43
- "css-loader": "^6.8.1",
43
+ "css-loader": "^6.11.0",
44
44
  "domain-browser": "^5.7.0",
45
45
  "events": "^3.3.0",
46
46
  "extra-watch-webpack-plugin": "^1.0.3",
47
47
  "fs-extra": "^11.2.0",
48
48
  "https-browserify": "^1.0.0",
49
- "inline-source-map": "^0.6.2",
49
+ "inline-source-map": "^0.6.3",
50
50
  "json-loader": "0.5.7",
51
51
  "jszip": "^3.10.1",
52
52
  "less": "^4.2.0",
53
- "less-loader": "^11.1.4",
53
+ "less-loader": "^12.2.0",
54
54
  "lodash.clonedeep": "4.5.0",
55
55
  "lodash.defaults": "4.2.0",
56
56
  "lodash.isequal": "4.5.0",
57
57
  "lodash.ismatch": "^4.4.0",
58
- "mini-css-extract-plugin": "^2.7.6",
58
+ "mini-css-extract-plugin": "^2.9.0",
59
59
  "os-browserify": "^0.3.0",
60
60
  "path-browserify": "^1.0.1",
61
61
  "powerbi-visuals-webpack-plugin": "4.1.0",
@@ -74,17 +74,17 @@
74
74
  "url": "^0.11.3",
75
75
  "util": "^0.12.5",
76
76
  "vm-browserify": "^1.1.2",
77
- "webpack": "^5.89.0",
78
- "webpack-bundle-analyzer": "4.10.1",
79
- "webpack-dev-server": "^4.15.1"
77
+ "webpack": "^5.91.0",
78
+ "webpack-bundle-analyzer": "4.10.2",
79
+ "webpack-dev-server": "^4.15.2"
80
80
  },
81
81
  "devDependencies": {
82
- "@typescript-eslint/eslint-plugin": "^6.17.0",
83
- "eslint": "^8.56.0",
82
+ "@typescript-eslint/eslint-plugin": "^7.12.0",
83
+ "eslint": "^8.57.0",
84
84
  "eslint-plugin-powerbi-visuals": "^0.8.1",
85
85
  "jasmine": "5.1.0",
86
86
  "jasmine-spec-reporter": "7.0.0",
87
- "semver": "7.5.4",
87
+ "semver": "7.6.2",
88
88
  "tree-kill": "1.2.2",
89
89
  "webpack-cli": "^5.1.4"
90
90
  },
@@ -1,28 +0,0 @@
1
- -----BEGIN PRIVATE KEY-----
2
- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCogcnCQF7icDMN
3
- Ot76I0U7W33z/d4kDirk525CT+uehSAEFqoNuz6fOXIsjDIz4nYrXusmcX2w3XTt
4
- SJr9vxvia437lE6wiNUUTmD8j2Xin3pjPa3j203suQQ+WZnbomidBDDgPTuEnqtY
5
- v/BDnT6i0DBtFu22CRDgdUK/zJRW6pJ1jikHDMJeEplaZXMYVTC3ZvyZXPQfvWzV
6
- EoIh4root7GyWOEPUuhZf2BfS2hP7ELTBG+EMFw5j7grO1uiNVy68pSMPTSv92Hn
7
- QmlGARlVsXlpCwyjHbmGU6UkHiP+LeMIKnSPIzbaHFz1TXBp93Tel/YyglSGx7UD
8
- eZr8Su4NAgMBAAECggEAAKfMluaifGyTctVebxqDlza7icPKHKLK5qLm9/d+nn7h
9
- W6T/yDMtea6nc69WfvC+N1xCO3qM0q+os2vgGC5yfBsztmaZgAvd7XamllvF3i+X
10
- n+0O2iz6tfnzH32Jl796xERzHssJnJaq2GQIdXCWN9vFUQYsc4rkxWmayTjstxRn
11
- CK0cEA42ObwABm1PXzkF1f4yX0K0EKxu+IgWcab2QM1QytuUXy5g3aqSgvHltfyu
12
- 7sAsvwbR0eDNT5errekwXp6P5p3Z32wGqtGPITvhXrDRO9dwdIqriYXONUeSjuUQ
13
- QQWcXMjUXjrWRmXeYVUT29qpA5RsTYB4ZPyKuZlQ4wKBgQDB8siSdtGRD624nDYU
14
- M+oRpCivJnIOXXTTZwt0LaHWepwDK/9DTlCGjKiBZ018arcDfEwCzPqKlkq9c4/k
15
- jCG94o+wrRNLmA8nJsDX/VzCUixsvypvnpmlTNL3gZuPrpcODJwi1GCnll9BKKrx
16
- euzPAprFiIl11vL9UvctxqHDfwKBgQDeaz5/hB7K7ns2Pr6+QjOwGE2lADgb90rk
17
- ovnz5BDIX5FdlYqp1FcL5VxlFZBg7evzFenG9p/F5A/ILtRvsCOMrNgAvg8YO17+
18
- lYdJMGMte7FPbU7ZbrEwXSqWTyqWbIRkSF6p/UZjKdqVkvbI5kLoD9Rv5yX2N7Kt
19
- wzkNBf3kcwKBgQCrH2uYzv1Y4FyG5k1Qxf1gU1cSMvmvcwYBAIo6l4iTlNrScZ9n
20
- 41FpZfHHOz5C6GVRzfnC/tiQLssDHVN4c6vRvnTJjevYZvH3uVmtOP7+9UH9MS5g
21
- Da/DCWWau+BlhcYBv16D3J7rPuIMu7UYGz2V6NZmM3IVZvI+UVWGLhvMQQKBgQDV
22
- mvFezag1BGvYYNIXLsUtLWoEOP2p/gkzI0Ih9LoZdY7J7cBJ8d/XUOM3QUpzjIu5
23
- 3SWzK0+uYaS2gCMNUIATrjuUGvOsNfi5TymqJIpbOBvFSBkdwKzy4cbwq/8ekAFl
24
- vgsL/m4G9j8yvv27W0iOTgWbosZWfI9hScgUtdhr+QKBgFHglSD6HJO3KMYzVYHQ
25
- UaZWBs3vRY651vd9uIa4+SD09++t8wpSnOw8BAVkC0H4B2u/H+uXqLkBjta4QlHn
26
- 6++Lg73bPmYVTvGwFpNeqaYmn2huP1vVr5Vayzjg9l0hoDCFnsFZMyfenhjlOCbm
27
- p2AItkHbaog5a/rLjV58yEXA
28
- -----END PRIVATE KEY-----
@@ -1,19 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIDCTCCAfGgAwIBAgIURq4z8w69FkvegPibl5y4/gy6j0gwDQYJKoZIhvcNAQEL
3
- BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MDIxMjE5MDE1MVoXDTI1MDIx
4
- MTE5MDE1MVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5
- AAOCAQ8AMIIBCgKCAQEAqIHJwkBe4nAzDTre+iNFO1t98/3eJA4q5OduQk/rnoUg
6
- BBaqDbs+nzlyLIwyM+J2K17rJnF9sN107Uia/b8b4muN+5ROsIjVFE5g/I9l4p96
7
- Yz2t49tN7LkEPlmZ26JonQQw4D07hJ6rWL/wQ50+otAwbRbttgkQ4HVCv8yUVuqS
8
- dY4pBwzCXhKZWmVzGFUwt2b8mVz0H71s1RKCIeK6KLexsljhD1LoWX9gX0toT+xC
9
- 0wRvhDBcOY+4KztbojVcuvKUjD00r/dh50JpRgEZVbF5aQsMox25hlOlJB4j/i3j
10
- CCp0jyM22hxc9U1wafd03pf2MoJUhse1A3ma/EruDQIDAQABo1MwUTAdBgNVHQ4E
11
- FgQUP6Yxfkh8EYZu8C30+jj+4mjOJQwwHwYDVR0jBBgwFoAUP6Yxfkh8EYZu8C30
12
- +jj+4mjOJQwwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANQdr
13
- HnpKoC7vgYEXEsnrZNRqAlTCUBSZlsBP9wvUCAS2lgZJTeNKiqONg7KlfTR0ob1P
14
- MqeqyYxMuEGYRmIglZPORvX7dN3bl/l6860RTV+ctzvVGszvOFGAUZ7YV2nEcTaR
15
- KTg17huZ9E9apt4P0wT5edUcAQdF10WuOuCZVs0jgIlLv1EOkzkB6QJLu/GGhaYH
16
- sBf6i0FBRnDtcBw9/20xIUqXFQb56bL0X+6TWOfDMTKcl99KrlHFtg2O0tFInNSc
17
- 0e10FLES58gKb2w7q1UyHpWWDLjCyP3SqB4lcpFaKYHUio0IvvFME3PfJMcNjurL
18
- X0ge18jFIOnvUvOiLw==
19
- -----END CERTIFICATE-----