powerbi-visuals-tools 4.3.2 → 5.0.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 +7 -0
- package/README.md +1 -1
- package/bin/pbiviz.js +55 -36
- package/certs/PowerBICustomVisualTest_private.key +26 -26
- package/certs/PowerBICustomVisualTest_public.crt +17 -17
- package/config.json +27 -34
- package/lib/CertificateTools.js +119 -143
- package/lib/CommandManager.js +52 -0
- package/lib/ConsoleWriter.js +63 -85
- package/lib/TemplateFetcher.js +23 -30
- package/lib/VisualGenerator.js +42 -56
- package/lib/VisualManager.js +193 -0
- package/lib/WebPackWrap.js +96 -145
- package/lib/utils.js +21 -13
- package/lib/webpack.config.js +47 -56
- package/package.json +34 -26
- package/spec/clean-tests.js +1 -1
- package/spec/e2e/pbivizCertSpec.js +14 -13
- package/spec/e2e/pbivizInfoSpec.js +7 -10
- package/spec/e2e/pbivizNewSpec.js +53 -65
- package/spec/e2e/pbivizPackageSpec.js +86 -90
- package/spec/e2e/pbivizStartSpec.js +6 -7
- package/spec/e2e/pbivizWebpackVerSpec.js +14 -16
- package/spec/e2e/{utils.js → testUtils.js} +9 -12
- package/spec/helpers/FileSystem.js +18 -18
- package/spec/jasmine-runner.js +5 -5
- package/src/CertificateTools.ts +431 -0
- package/src/CommandManager.ts +78 -0
- package/src/ConsoleWriter.ts +206 -0
- package/src/TemplateFetcher.ts +122 -0
- package/src/VisualGenerator.ts +236 -0
- package/src/VisualManager.ts +220 -0
- package/src/WebPackWrap.ts +299 -0
- package/src/utils.ts +41 -0
- package/src/webpack.config.ts +144 -0
- package/templates/pbiviz-json-template.js +2 -2
- package/templates/pbiviz.json.template +1 -1
- package/templates/plugin-ts-template.js +1 -1
- package/templates/visuals/default/.eslintignore +5 -0
- package/templates/visuals/default/.eslintrc.js +20 -0
- package/templates/visuals/default/package.json +9 -8
- package/templates/visuals/default/pbiviz.json +3 -2
- package/templates/visuals/default/tsconfig.json +2 -2
- package/templates/visuals/rhtml/.eslintignore +5 -0
- package/templates/visuals/rhtml/.eslintrc.js +20 -0
- package/templates/visuals/rhtml/package.json +7 -6
- package/templates/visuals/rhtml/pbiviz.json +2 -1
- package/templates/visuals/rvisual/.eslintignore +5 -0
- package/templates/visuals/rvisual/.eslintrc.js +20 -0
- package/templates/visuals/rvisual/package.json +5 -4
- package/templates/visuals/slicer/.eslintignore +5 -0
- package/templates/visuals/slicer/.eslintrc.js +20 -0
- package/templates/visuals/slicer/package.json +8 -7
- package/templates/visuals/table/.eslintignore +5 -0
- package/templates/visuals/table/.eslintrc.js +20 -0
- package/templates/visuals/table/package.json +8 -7
- package/templates/visuals/table/tsconfig.json +4 -0
- package/tsconfig.json +22 -0
- package/bin/pbiviz-info.js +0 -54
- package/bin/pbiviz-new.js +0 -82
- package/bin/pbiviz-package.js +0 -122
- package/bin/pbiviz-start.js +0 -142
- package/lib/CommandHelpManager.js +0 -51
- package/lib/VisualPackage.js +0 -118
- package/templates/visuals/default/tslint.json +0 -9
- package/templates/visuals/rhtml/tslint.json +0 -9
- package/templates/visuals/rvisual/tslint.json +0 -9
- package/templates/visuals/slicer/tslint.json +0 -9
- package/templates/visuals/table/tslint.json +0 -9
package/lib/ConsoleWriter.js
CHANGED
|
@@ -23,76 +23,64 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
24
|
* THE SOFTWARE.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
26
|
"use strict";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (os.platform() === 'darwin') {
|
|
35
|
-
chalk = chalk.bold;
|
|
36
|
-
}
|
|
37
|
-
|
|
27
|
+
import chalk from 'chalk';
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import os from 'os';
|
|
30
|
+
import path from 'path';
|
|
31
|
+
import { getRootPath } from './utils.js';
|
|
32
|
+
const preferredChalk = os.platform() === 'darwin' ? chalk.bold : chalk;
|
|
38
33
|
function prependLogTag(tag, args) {
|
|
39
|
-
return [tag].concat(
|
|
34
|
+
return [tag].concat(args);
|
|
40
35
|
}
|
|
41
|
-
|
|
42
|
-
class ConsoleWriter {
|
|
36
|
+
export default class ConsoleWriter {
|
|
43
37
|
/** Causes the terminal to beep */
|
|
44
38
|
static beep() {
|
|
45
39
|
process.stdout.write("\x07");
|
|
46
40
|
}
|
|
47
|
-
|
|
48
41
|
/** Outputs a blank line */
|
|
49
42
|
static blank() {
|
|
50
|
-
console.info(
|
|
43
|
+
console.info(preferredChalk.reset(' '));
|
|
51
44
|
}
|
|
52
|
-
|
|
53
45
|
/**
|
|
54
46
|
* Outputs arguments with the "done" tag / colors
|
|
55
|
-
*
|
|
56
|
-
* @param {
|
|
47
|
+
*
|
|
48
|
+
* @param {array} arguments - arguments passed through to console.info
|
|
57
49
|
*/
|
|
58
|
-
static done(
|
|
59
|
-
|
|
60
|
-
console.info.apply(this, prependLogTag(tag,
|
|
50
|
+
static done(args) {
|
|
51
|
+
const tag = preferredChalk.bgGreen(' done ');
|
|
52
|
+
console.info.apply(this, prependLogTag(tag, args));
|
|
61
53
|
}
|
|
62
|
-
|
|
63
54
|
/**
|
|
64
55
|
* Outputs arguments with the "info" tag / colors
|
|
65
|
-
*
|
|
66
|
-
* @param {
|
|
56
|
+
*
|
|
57
|
+
* @param {array} args - arguments passed through to console.info
|
|
67
58
|
*/
|
|
68
|
-
static info(
|
|
69
|
-
|
|
70
|
-
console.info.apply(this, prependLogTag(tag,
|
|
59
|
+
static info(args) {
|
|
60
|
+
const tag = preferredChalk.bgCyan(' info ');
|
|
61
|
+
console.info.apply(this, prependLogTag(tag, args));
|
|
71
62
|
}
|
|
72
|
-
|
|
73
63
|
/**
|
|
74
64
|
* Outputs arguments with the "warn" tag / colors
|
|
75
|
-
*
|
|
76
|
-
* @param {
|
|
65
|
+
*
|
|
66
|
+
* @param {array} args - arguments passed through to console.warn
|
|
77
67
|
*/
|
|
78
|
-
static
|
|
79
|
-
|
|
80
|
-
console.warn.apply(this, prependLogTag(tag,
|
|
68
|
+
static warning(args) {
|
|
69
|
+
const tag = preferredChalk.bgYellow.black(' warn ');
|
|
70
|
+
console.warn.apply(this, prependLogTag(tag, args));
|
|
81
71
|
}
|
|
82
|
-
|
|
83
72
|
/**
|
|
84
73
|
* Outputs arguments with the "error" tag / colors
|
|
85
|
-
*
|
|
86
|
-
* @param {
|
|
74
|
+
*
|
|
75
|
+
* @param {array} args - arguments passed through to console.error
|
|
87
76
|
*/
|
|
88
|
-
static error(
|
|
89
|
-
|
|
90
|
-
console.error.apply(this, prependLogTag(tag,
|
|
77
|
+
static error(args) {
|
|
78
|
+
const tag = preferredChalk.bgRed(' error ');
|
|
79
|
+
console.error.apply(this, prependLogTag(tag, args));
|
|
91
80
|
}
|
|
92
|
-
|
|
93
81
|
/**
|
|
94
82
|
* Outputs an object as a table
|
|
95
|
-
*
|
|
83
|
+
*
|
|
96
84
|
* @param {string} data - object to output
|
|
97
85
|
* @param {number} [depthLimit=Infinity] - limit the number of levels to recurse
|
|
98
86
|
* @param {string} [keyPrefix=''] - text to prepend to each key
|
|
@@ -101,35 +89,34 @@ class ConsoleWriter {
|
|
|
101
89
|
if (!data) {
|
|
102
90
|
return;
|
|
103
91
|
}
|
|
104
|
-
|
|
105
|
-
for (
|
|
106
|
-
|
|
107
|
-
|
|
92
|
+
const limit = typeof depthLimit === 'undefined' ? Infinity : depthLimit;
|
|
93
|
+
for (const key in data) {
|
|
94
|
+
const item = data[key];
|
|
95
|
+
const itemKey = (keyPrefix || '') + key;
|
|
108
96
|
if (limit > 1 && typeof item === 'object' && !Array.isArray(item)) {
|
|
109
97
|
ConsoleWriter.infoTable(item, limit - 1, itemKey + '.');
|
|
110
|
-
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
111
100
|
ConsoleWriter.infoTableRow(itemKey, item);
|
|
112
101
|
}
|
|
113
102
|
}
|
|
114
103
|
}
|
|
115
|
-
|
|
116
104
|
/**
|
|
117
105
|
* Outputs a table row with even spaced keys
|
|
118
|
-
*
|
|
106
|
+
*
|
|
119
107
|
* @param {string} key - title of this row
|
|
120
108
|
* @param {string} value - value for this row
|
|
121
109
|
* @param {number} [keyWidth=30] - width used for padding of the key column
|
|
122
110
|
*/
|
|
123
111
|
static infoTableRow(key, value, keyWidth) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
ConsoleWriter.info(paddedKey, value);
|
|
112
|
+
const width = keyWidth || 30;
|
|
113
|
+
const padding = Math.max(0, width - key.length);
|
|
114
|
+
const paddedKey = preferredChalk.bold(key) + (new Array(padding)).join('.');
|
|
115
|
+
ConsoleWriter.info([paddedKey, value]);
|
|
128
116
|
}
|
|
129
|
-
|
|
130
117
|
/**
|
|
131
118
|
* Outputs formatted errors
|
|
132
|
-
*
|
|
119
|
+
*
|
|
133
120
|
* @param {Array<Error>} errors
|
|
134
121
|
*/
|
|
135
122
|
static formattedErrors(errors) {
|
|
@@ -138,58 +125,53 @@ class ConsoleWriter {
|
|
|
138
125
|
if (!error) {
|
|
139
126
|
return;
|
|
140
127
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
ConsoleWriter.error(tag, `${file} ${position} ${message}`);
|
|
128
|
+
const tag = error.type ? preferredChalk.bold(error.type.toUpperCase()) : 'UNKNOWN';
|
|
129
|
+
const file = error.filename ? preferredChalk.bgWhite.black(` ${error.filename} `) + ':' : '';
|
|
130
|
+
const position = (error.line && error.column) ? preferredChalk.cyan(`(${error.line},${error.column})`) : '';
|
|
131
|
+
const message = error.message || '';
|
|
132
|
+
ConsoleWriter.error([tag, `${file} ${position} ${message}`]);
|
|
146
133
|
});
|
|
147
|
-
}
|
|
148
|
-
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
ConsoleWriter.error(['UNKNOWN', errors]);
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
|
-
|
|
152
139
|
/**
|
|
153
140
|
* Outputs ascii art of the PowerBI logo
|
|
154
141
|
*/
|
|
155
|
-
static
|
|
156
|
-
|
|
157
|
-
console.info(chalk.bold.yellow(logoText));
|
|
142
|
+
static getLogoVisualization() {
|
|
143
|
+
return fs.readFileSync(path.join(getRootPath(), 'assets', 'logo.txt')).toString();
|
|
158
144
|
}
|
|
159
|
-
|
|
160
145
|
/**
|
|
161
146
|
* Outputs validation log from PBIVIZ package checking
|
|
162
147
|
*/
|
|
163
148
|
static validationLog(log) {
|
|
164
|
-
|
|
165
149
|
// api/js/css/pkg
|
|
166
|
-
|
|
167
|
-
for (
|
|
150
|
+
const filterChecks = (attrCB, propCB) => {
|
|
151
|
+
for (const checkname in log) {
|
|
168
152
|
if (checkname !== 'valid') {
|
|
169
|
-
|
|
153
|
+
const checkpoint = log[checkname];
|
|
170
154
|
ConsoleWriter[checkpoint.error.length ? 'info' : 'done'](checkpoint.check);
|
|
171
155
|
attrCB(checkpoint, propCB);
|
|
172
156
|
}
|
|
173
157
|
}
|
|
174
158
|
};
|
|
175
|
-
|
|
176
159
|
// error/message/ok
|
|
177
|
-
|
|
178
|
-
for (
|
|
160
|
+
const filterCheckAttrs = (checkpoint, propCB) => {
|
|
161
|
+
for (const propName in checkpoint) {
|
|
179
162
|
if (propName !== 'message') {
|
|
180
|
-
|
|
163
|
+
const prop = checkpoint[propName];
|
|
181
164
|
if (typeof (prop) === 'object' && prop.length) {
|
|
182
165
|
propCB(prop, propName);
|
|
183
166
|
}
|
|
184
167
|
}
|
|
185
168
|
}
|
|
186
169
|
};
|
|
187
|
-
|
|
188
170
|
// col/line/text
|
|
189
|
-
|
|
171
|
+
const filterAttrProps = (props, propName) => {
|
|
190
172
|
props.forEach((opt) => {
|
|
191
|
-
|
|
192
|
-
for (
|
|
173
|
+
const result = [];
|
|
174
|
+
for (const key in opt) {
|
|
193
175
|
result.push(opt[key]);
|
|
194
176
|
}
|
|
195
177
|
if (result.length) {
|
|
@@ -197,14 +179,10 @@ class ConsoleWriter {
|
|
|
197
179
|
}
|
|
198
180
|
});
|
|
199
181
|
};
|
|
200
|
-
|
|
201
182
|
filterChecks(filterCheckAttrs, filterAttrProps);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let text = log.valid ? 'Valid package' : 'Invalid package';
|
|
183
|
+
const type = log.valid ? 'done' : 'error';
|
|
184
|
+
const text = log.valid ? 'Valid package' : 'Invalid package';
|
|
205
185
|
ConsoleWriter.blank();
|
|
206
186
|
ConsoleWriter[type](text);
|
|
207
187
|
}
|
|
208
188
|
}
|
|
209
|
-
|
|
210
|
-
module.exports = ConsoleWriter;
|
package/lib/TemplateFetcher.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { createFolder, download, readJsonFromRoot } from './utils.js';
|
|
2
|
+
import ConsoleWriter from './ConsoleWriter.js';
|
|
3
|
+
import JSZip from 'jszip';
|
|
4
|
+
import VisualGenerator from "./VisualGenerator.js";
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
const config = readJsonFromRoot('config.json');
|
|
9
|
+
export default class TemplateFetcher {
|
|
10
|
+
templateName;
|
|
11
|
+
visualName;
|
|
12
|
+
folderName;
|
|
13
|
+
apiVersion;
|
|
14
|
+
constructor(templateName, visualName, apiVersion) {
|
|
13
15
|
this.templateName = templateName;
|
|
14
16
|
this.visualName = visualName;
|
|
15
17
|
this.folderName = `${this.visualName}`;
|
|
16
18
|
this.apiVersion = apiVersion;
|
|
17
19
|
}
|
|
18
|
-
|
|
19
20
|
fetch() {
|
|
20
|
-
|
|
21
|
+
const folder = createFolder.call(this, this.folderName);
|
|
21
22
|
download.call(this, config.visualTemplates[this.templateName], path.join(folder, "template.zip"))
|
|
22
23
|
.then(this.extractFiles.bind(this))
|
|
23
24
|
.then(this.removeZipFile.bind(this))
|
|
@@ -26,37 +27,34 @@ class TemplateFetcher {
|
|
|
26
27
|
.then(this.runNpmInstall.bind(this))
|
|
27
28
|
.then(this.showNextSteps.bind(this));
|
|
28
29
|
}
|
|
29
|
-
|
|
30
30
|
async removeZipFile() {
|
|
31
31
|
const folder = path.join("./", this.folderName);
|
|
32
32
|
const fileName = path.join(folder, "template.zip");
|
|
33
33
|
await fs.unlink(`.${path.sep}${fileName}`, (err) => {
|
|
34
34
|
if (err) {
|
|
35
|
-
ConsoleWriter.
|
|
35
|
+
ConsoleWriter.warning(`.${path.sep}${fileName} was not deleted`);
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
-
|
|
40
39
|
async extractFiles(file) {
|
|
41
40
|
const filePath = path.join(process.cwd(), file.path);
|
|
42
41
|
const buffer = await fs.readFile(filePath);
|
|
43
42
|
const zip = await JSZip.loadAsync(buffer);
|
|
44
|
-
|
|
45
43
|
const filesList = Object.keys(zip.files);
|
|
46
44
|
for (const filename of filesList) {
|
|
47
45
|
if (filename[filename.length - 1] === "/") {
|
|
48
46
|
// generate folders for exclude parent folder
|
|
49
47
|
const dest = path.join(path.dirname(filePath), path.join(filename, ".."));
|
|
50
48
|
await fs.ensureDir(dest);
|
|
51
|
-
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
52
51
|
// write files into dirs for exclude parent folder
|
|
53
|
-
const dest = path.join(path.dirname(filePath), path.join(path.dirname(filename), "..", filename.split("/").pop(
|
|
54
|
-
const content = await zip.file(filename)
|
|
52
|
+
const dest = path.join(path.dirname(filePath), path.join(path.dirname(filename), "..", filename.split("/").pop() ?? ""));
|
|
53
|
+
const content = await zip.file(filename)?.async('nodebuffer');
|
|
55
54
|
await fs.writeFile(dest, content);
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
|
-
|
|
60
58
|
async setApiVersion() {
|
|
61
59
|
if (!this.apiVersion) {
|
|
62
60
|
return;
|
|
@@ -72,14 +70,12 @@ class TemplateFetcher {
|
|
|
72
70
|
}
|
|
73
71
|
await fs.writeJSON(packageJsonFile, packageJson);
|
|
74
72
|
}
|
|
75
|
-
|
|
76
73
|
async setVisualGUID() {
|
|
77
74
|
const pbivizJsonFile = path.join(process.cwd(), this.folderName, "pbiviz.json");
|
|
78
75
|
const pbivizJson = await fs.readJson(pbivizJsonFile);
|
|
79
76
|
pbivizJson.visual.guid = this.visualName + VisualGenerator.generateVisualGuid();
|
|
80
77
|
await fs.writeJSON(pbivizJsonFile, pbivizJson);
|
|
81
78
|
}
|
|
82
|
-
|
|
83
79
|
runNpmInstall() {
|
|
84
80
|
return new Promise((resolve, reject) => {
|
|
85
81
|
ConsoleWriter.info("Installing packages...");
|
|
@@ -91,12 +87,12 @@ class TemplateFetcher {
|
|
|
91
87
|
ConsoleWriter.error(`Error code: ${error.code}`);
|
|
92
88
|
ConsoleWriter.error(`Signal received: ${error.signal}`);
|
|
93
89
|
}
|
|
94
|
-
ConsoleWriter.
|
|
90
|
+
ConsoleWriter.warning(stderr);
|
|
95
91
|
ConsoleWriter.info(stdout);
|
|
96
|
-
resolve();
|
|
92
|
+
resolve(true);
|
|
97
93
|
});
|
|
98
94
|
child.on("error", (er) => {
|
|
99
|
-
ConsoleWriter.
|
|
95
|
+
ConsoleWriter.error(er);
|
|
100
96
|
reject();
|
|
101
97
|
});
|
|
102
98
|
child.on("exit", (code) => {
|
|
@@ -107,12 +103,9 @@ class TemplateFetcher {
|
|
|
107
103
|
});
|
|
108
104
|
});
|
|
109
105
|
}
|
|
110
|
-
|
|
111
106
|
showNextSteps() {
|
|
112
107
|
ConsoleWriter.blank();
|
|
113
108
|
ConsoleWriter.info("Run `npm run start` to start visual development");
|
|
114
109
|
ConsoleWriter.info("Run `npm run package` to create visual package");
|
|
115
110
|
}
|
|
116
111
|
}
|
|
117
|
-
|
|
118
|
-
module.exports = TemplateFetcher;
|
package/lib/VisualGenerator.js
CHANGED
|
@@ -23,21 +23,18 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
24
24
|
* THE SOFTWARE.
|
|
25
25
|
*/
|
|
26
|
-
|
|
27
26
|
"use strict";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
const VISUAL_TEMPLATES_PATH = path.join(__dirname, '..', config.templates.visuals);
|
|
27
|
+
import crypto from 'crypto';
|
|
28
|
+
import { getRootPath, readJsonFromRoot } from './utils.js';
|
|
29
|
+
import { compareVersions } from "compare-versions";
|
|
30
|
+
import fs from 'fs-extra';
|
|
31
|
+
import lodashDefaults from 'lodash.defaults';
|
|
32
|
+
import path from 'path';
|
|
33
|
+
import template from '../templates/pbiviz-json-template.js';
|
|
34
|
+
const config = readJsonFromRoot('config.json');
|
|
35
|
+
const VISUAL_TEMPLATES_PATH = path.join(getRootPath(), config.templates.visuals);
|
|
38
36
|
const API_VERSION = config.generate.apiVersion;
|
|
39
37
|
const minAPIversion = config.constants.minAPIversion;
|
|
40
|
-
|
|
41
38
|
/**
|
|
42
39
|
* Generates the data for the visual
|
|
43
40
|
*/
|
|
@@ -51,9 +48,8 @@ function generateVisualOptions(visualName, apiVersion) {
|
|
|
51
48
|
apiVersion: apiVersion
|
|
52
49
|
};
|
|
53
50
|
}
|
|
54
|
-
|
|
55
51
|
/**
|
|
56
|
-
*
|
|
52
|
+
*
|
|
57
53
|
*/
|
|
58
54
|
function generateVisualName(displayName) {
|
|
59
55
|
return displayName.replace(/(?:^\w|[A-Z]|\b\w|_|\s+)/g, (match, index) => {
|
|
@@ -63,58 +59,51 @@ function generateVisualName(displayName) {
|
|
|
63
59
|
return index === 0 ? match.toLowerCase() : match.toUpperCase();
|
|
64
60
|
});
|
|
65
61
|
}
|
|
66
|
-
|
|
67
62
|
/**
|
|
68
63
|
* Creates a default pbiviz.json config file
|
|
69
|
-
*
|
|
64
|
+
*
|
|
70
65
|
* @param {string} visualPath - path to the visual
|
|
71
66
|
* @param {Object} options - visual information for populating the pbiviz.json template
|
|
72
67
|
* @param {string} templateName - external js files
|
|
73
68
|
*/
|
|
74
69
|
function createPbiVizJson(visualPath, options, templateName) {
|
|
75
|
-
|
|
76
70
|
// read the global template data
|
|
77
71
|
// and generate the actual file content
|
|
78
72
|
let data = template(options);
|
|
79
|
-
|
|
80
73
|
// write out the target file content
|
|
81
|
-
|
|
74
|
+
const targetPath = path.join(visualPath, 'pbiviz.json');
|
|
82
75
|
fs.writeFileSync(targetPath, data);
|
|
83
|
-
|
|
84
76
|
let templatePath = path.join(VISUAL_TEMPLATES_PATH, templateName);
|
|
85
77
|
templatePath = path.join(templatePath, 'pbiviz.json');
|
|
86
78
|
if (templateName && fileExists(templatePath)) {
|
|
87
79
|
//read the target file content
|
|
88
80
|
data = fs.readJsonSync(targetPath);
|
|
89
|
-
|
|
90
81
|
//override externalJS settings with those of the local template file
|
|
91
|
-
|
|
82
|
+
const templateData = fs.readJsonSync(templatePath);
|
|
92
83
|
for (const objKey of Object.keys(templateData)) {
|
|
93
84
|
data[objKey] = templateData[objKey];
|
|
94
85
|
}
|
|
95
|
-
|
|
96
86
|
// write out the target file content
|
|
97
87
|
fs.writeJsonSync(targetPath, data);
|
|
98
88
|
}
|
|
99
89
|
}
|
|
100
|
-
|
|
101
90
|
/**
|
|
102
91
|
* Checks if the specified file exists
|
|
103
|
-
*
|
|
104
|
-
* @param {string} file - path to the file
|
|
92
|
+
*
|
|
93
|
+
* @param {string} file - path to the file
|
|
105
94
|
*/
|
|
106
95
|
function fileExists(file) {
|
|
107
96
|
try {
|
|
108
97
|
fs.accessSync(file);
|
|
109
|
-
}
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
110
100
|
return false;
|
|
111
101
|
}
|
|
112
102
|
return true;
|
|
113
103
|
}
|
|
114
|
-
|
|
115
104
|
/**
|
|
116
105
|
* Copies the visual template directory
|
|
117
|
-
*
|
|
106
|
+
*
|
|
118
107
|
* @param {string} targetPath - file path to root of visual package
|
|
119
108
|
* @param {string} templateName - template to use for generating the visual
|
|
120
109
|
*/
|
|
@@ -122,54 +111,50 @@ function copyVisualTemplate(targetPath, templateName) {
|
|
|
122
111
|
fs.copySync(path.join(VISUAL_TEMPLATES_PATH, '_global'), targetPath);
|
|
123
112
|
fs.copySync(path.join(VISUAL_TEMPLATES_PATH, templateName), targetPath);
|
|
124
113
|
}
|
|
125
|
-
|
|
126
114
|
/**
|
|
127
115
|
* Checks if the specified template is valid
|
|
128
|
-
*
|
|
116
|
+
*
|
|
129
117
|
* @param {string} templateName - template to use for generating the visual
|
|
130
118
|
*/
|
|
131
119
|
function validTemplate(templateName) {
|
|
132
120
|
try {
|
|
133
121
|
fs.accessSync(path.join(VISUAL_TEMPLATES_PATH, templateName));
|
|
134
|
-
}
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
135
124
|
return false;
|
|
136
125
|
}
|
|
137
126
|
return true;
|
|
138
127
|
}
|
|
139
|
-
|
|
140
128
|
const defaultOptions = {
|
|
141
129
|
force: false,
|
|
142
130
|
template: 'default',
|
|
143
131
|
apiVersion: API_VERSION,
|
|
144
132
|
externalJS: []
|
|
145
133
|
};
|
|
146
|
-
|
|
147
|
-
class VisualGenerator {
|
|
134
|
+
export default class VisualGenerator {
|
|
148
135
|
/**
|
|
149
136
|
* Generates a new visual
|
|
150
|
-
*
|
|
137
|
+
*
|
|
151
138
|
* @param {string} targetPath - file path for creation of the new visual package
|
|
152
139
|
* @param {string} visualName - name of the new visual package
|
|
153
140
|
* @param {object} options - specify options for the visual generator
|
|
154
|
-
* @returns {Promise<string>} - promise resolves with the path to the newly created package
|
|
141
|
+
* @returns {Promise<string>} - promise resolves with the path to the newly created package
|
|
155
142
|
*/
|
|
156
|
-
static
|
|
143
|
+
static generateVisual(targetPath, visualName, options) {
|
|
157
144
|
return new Promise((resolve, reject) => {
|
|
158
|
-
|
|
159
|
-
if (!buildOptions.apiVersion || compareVersions
|
|
145
|
+
const buildOptions = lodashDefaults(options, defaultOptions);
|
|
146
|
+
if (!buildOptions.apiVersion || compareVersions(buildOptions.apiVersion, minAPIversion) === -1) {
|
|
160
147
|
return reject(new Error(`Can not generate a visual with an API below than ${minAPIversion}, current API is '${buildOptions.apiVersion}'.`));
|
|
161
148
|
}
|
|
162
|
-
|
|
149
|
+
const visualOptions = generateVisualOptions(visualName, buildOptions.apiVersion);
|
|
163
150
|
const validationResult = VisualGenerator.checkVisualName(visualOptions.name);
|
|
164
151
|
if (!visualOptions || !visualOptions.name || validationResult) {
|
|
165
152
|
return reject(new Error(validationResult || "Invalid visual name"));
|
|
166
153
|
}
|
|
167
|
-
|
|
168
154
|
if (!validTemplate(buildOptions.template)) {
|
|
169
|
-
return reject(new Error(
|
|
155
|
+
return reject(new Error(`Invalid template "${buildOptions.template}"`));
|
|
170
156
|
}
|
|
171
|
-
|
|
172
|
-
let visualPath = path.join(targetPath, visualOptions.name);
|
|
157
|
+
const visualPath = path.join(targetPath, visualOptions.name);
|
|
173
158
|
fs.access(visualPath, err => {
|
|
174
159
|
if (!err && !buildOptions.force) {
|
|
175
160
|
return reject(new Error('This visual already exists. Use force to overwrite.'));
|
|
@@ -181,28 +166,27 @@ class VisualGenerator {
|
|
|
181
166
|
copyVisualTemplate(visualPath, buildOptions.template);
|
|
182
167
|
createPbiVizJson(visualPath, visualOptions, options.template);
|
|
183
168
|
resolve(visualPath);
|
|
184
|
-
}
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
185
171
|
reject(e);
|
|
186
172
|
}
|
|
187
173
|
});
|
|
188
174
|
});
|
|
189
175
|
}
|
|
190
|
-
|
|
191
176
|
/**
|
|
192
177
|
* Generates a random GUID for your visual
|
|
193
178
|
*/
|
|
194
179
|
static generateVisualGuid() {
|
|
195
|
-
return
|
|
180
|
+
return crypto.randomUUID().replace(/-/g, '').toUpperCase();
|
|
196
181
|
}
|
|
197
|
-
|
|
198
182
|
/**
|
|
199
|
-
* Check visual name
|
|
183
|
+
* Check visual name
|
|
200
184
|
* Using https://github.com/mathiasbynens/mothereff.in/tree/master/js-properties
|
|
201
|
-
*
|
|
185
|
+
*
|
|
202
186
|
* @static
|
|
203
187
|
* @param {string} name Visual name
|
|
204
188
|
* @returns {string} error message
|
|
205
|
-
*
|
|
189
|
+
*
|
|
206
190
|
* @memberof VisualGenerator
|
|
207
191
|
*/
|
|
208
192
|
static checkVisualName(name) {
|
|
@@ -222,14 +206,16 @@ class VisualGenerator {
|
|
|
222
206
|
});
|
|
223
207
|
if (regexNumber.test(name)) {
|
|
224
208
|
return `The visual name can't begin with a number digit`;
|
|
225
|
-
}
|
|
209
|
+
}
|
|
210
|
+
else if (!regexpWrongSymbols.test(name)) {
|
|
226
211
|
return `The visual name can contain only letters and numbers`;
|
|
227
|
-
}
|
|
212
|
+
}
|
|
213
|
+
else if (regexES3ReservedWord.test(valueAsUnescapedString)) {
|
|
228
214
|
return `The visual name cannot be equal to a reserved JavaScript keyword.
|
|
229
215
|
More information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords`;
|
|
230
|
-
}
|
|
216
|
+
}
|
|
217
|
+
else if (regexZeroWidth.test(valueAsUnescapedString)) {
|
|
231
218
|
return `The visual name can't be empty`;
|
|
232
219
|
}
|
|
233
220
|
}
|
|
234
221
|
}
|
|
235
|
-
module.exports = VisualGenerator;
|