@flourish/sdk 3.14.1 → 3.17.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/RELEASE_NOTES.md +20 -0
- package/audit-resolve.json +36 -0
- package/bin/flourish +5 -0
- package/lib/cmd/assign-version-number.js +1 -1
- package/lib/cmd/help.js +1 -1
- package/lib/cmd/publish.js +31 -12
- package/lib/cmd/run.js +2 -1
- package/lib/cmd/version.js +1 -1
- package/lib/log.js +1 -1
- package/lib/sdk.js +101 -70
- package/lib/validate_config.js +7 -6
- package/package.json +30 -22
- package/server/columns.js +59 -12
- package/server/comms_js.js +3 -5
- package/server/data.js +146 -9
- package/server/index.js +52 -20
- package/server/index_html.js +17 -8
- package/server/json.js +27 -1
- package/server/views/index.html +12 -11
- package/site/embedded.js +1 -1
- package/site/images/data_type_date.svg +6 -0
- package/site/images/data_type_number.svg +5 -0
- package/site/images/data_type_region.svg +4 -0
- package/site/images/data_type_string.svg +5 -0
- package/site/images/folder_icon.png +0 -0
- package/site/images/montage.jpg +0 -0
- package/site/images/open_folder_icon.png +0 -0
- package/site/script.js +2 -1
- package/site/sdk.css +1 -1
- package/site/talk_to_server.js +1 -1
- package/test/lib/sdk.js +9 -0
- package/test/lib/validate_config.js +7 -5
- package/utils/state.js +2 -0
- package/test/mocha.opts +0 -2
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
# 3.17.0
|
|
2
|
+
|
|
3
|
+
* Allow published visualisations to be cloned into the SDK
|
|
4
|
+
|
|
5
|
+
# 3.16.0
|
|
6
|
+
* Upgrade all NPM dependencies.
|
|
7
|
+
|
|
8
|
+
# 3.15.1
|
|
9
|
+
* Avoid treating numeric-looking arguments as numbers.
|
|
10
|
+
|
|
11
|
+
# 3.15.0
|
|
12
|
+
|
|
13
|
+
* Reinstate preview link
|
|
14
|
+
* Remove template author info from side panel
|
|
15
|
+
* Fix z-index issue on resize bar
|
|
16
|
+
|
|
17
|
+
# 3.14.2
|
|
18
|
+
|
|
19
|
+
* Improve error checking of conditional-property overrides
|
|
20
|
+
|
|
1
21
|
# 3.14.1
|
|
2
22
|
|
|
3
23
|
* Remove stray build logging. #64
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"decisions": {
|
|
3
|
+
"1002401|npm-audit-resolver>yargs-unparser>yargs>string-width>strip-ansi>ansi-regex": {
|
|
4
|
+
"decision": "ignore",
|
|
5
|
+
"madeAt": 1637059117447,
|
|
6
|
+
"expiresAt": 1637663913365
|
|
7
|
+
},
|
|
8
|
+
"1002401|npm-audit-resolver>yargs-unparser>yargs>cliui>string-width>strip-ansi>ansi-regex": {
|
|
9
|
+
"decision": "ignore",
|
|
10
|
+
"madeAt": 1637059117447,
|
|
11
|
+
"expiresAt": 1637663913365
|
|
12
|
+
},
|
|
13
|
+
"1002401|npm-audit-resolver>yargs-unparser>yargs>cliui>wrap-ansi>string-width>strip-ansi>ansi-regex": {
|
|
14
|
+
"decision": "ignore",
|
|
15
|
+
"madeAt": 1637059117447,
|
|
16
|
+
"expiresAt": 1637663913365
|
|
17
|
+
},
|
|
18
|
+
"1004946|npm-audit-resolver>yargs-unparser>yargs>string-width>strip-ansi>ansi-regex": {
|
|
19
|
+
"decision": "ignore",
|
|
20
|
+
"madeAt": 1639560165214,
|
|
21
|
+
"expiresAt": 1642152161687
|
|
22
|
+
},
|
|
23
|
+
"1004946|npm-audit-resolver>yargs-unparser>yargs>cliui>string-width>strip-ansi>ansi-regex": {
|
|
24
|
+
"decision": "ignore",
|
|
25
|
+
"madeAt": 1639560165214,
|
|
26
|
+
"expiresAt": 1642152161687
|
|
27
|
+
},
|
|
28
|
+
"1004946|npm-audit-resolver>yargs-unparser>yargs>cliui>wrap-ansi>string-width>strip-ansi>ansi-regex": {
|
|
29
|
+
"decision": "ignore",
|
|
30
|
+
"madeAt": 1639560165214,
|
|
31
|
+
"expiresAt": 1642152161687
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"rules": {},
|
|
35
|
+
"version": 1
|
|
36
|
+
}
|
package/bin/flourish
CHANGED
|
@@ -57,6 +57,11 @@ process.on("unhandledRejection", function(reason, p) {
|
|
|
57
57
|
|
|
58
58
|
function main() {
|
|
59
59
|
const args = minimist(process.argv.slice(2), OPTS);
|
|
60
|
+
|
|
61
|
+
// minimist unhelpfully treats numeric strings as numbers;
|
|
62
|
+
// which means we have to turn them back into strings.
|
|
63
|
+
args._ = args._.map(x => "" + x);
|
|
64
|
+
|
|
60
65
|
let command = args._[0];
|
|
61
66
|
|
|
62
67
|
const server_opts = {
|
|
@@ -9,7 +9,7 @@ function assign_version_number(args, server_opts) {
|
|
|
9
9
|
let template_id_promise, template_version;
|
|
10
10
|
if (args._.length == 2) {
|
|
11
11
|
// Assume the supplied argument is a version number, and try to get the id from the current directory
|
|
12
|
-
template_id_promise = sdk.readAndValidateConfig(".").then(config => config.id);
|
|
12
|
+
template_id_promise = sdk.readAndValidateConfig(".").then(({config}) => config.id);
|
|
13
13
|
template_version = args._[1];
|
|
14
14
|
}
|
|
15
15
|
else if (args._.length == 3) {
|
package/lib/cmd/help.js
CHANGED
|
@@ -24,7 +24,7 @@ help.help = `
|
|
|
24
24
|
Commands:
|
|
25
25
|
flourish assign-version-number [template id] version
|
|
26
26
|
flourish build [build rules...]
|
|
27
|
-
flourish delete [--force] template_id
|
|
27
|
+
flourish delete [--force] template_id version
|
|
28
28
|
flourish [-h|--help|help] [topic]
|
|
29
29
|
flourish history [--full] template_id
|
|
30
30
|
flourish list [--full] [template id]
|
package/lib/cmd/publish.js
CHANGED
|
@@ -5,9 +5,13 @@ var fs = require("fs"),
|
|
|
5
5
|
|
|
6
6
|
archiver = require("archiver"),
|
|
7
7
|
tmp = require("tmp"),
|
|
8
|
+
FormData = require("form-data"),
|
|
9
|
+
|
|
10
|
+
d3_dsv = require("d3-dsv"),
|
|
8
11
|
|
|
9
12
|
log = require("../log"),
|
|
10
|
-
sdk = require("../sdk")
|
|
13
|
+
sdk = require("../sdk"),
|
|
14
|
+
data_utils = require("../../server/data");
|
|
11
15
|
|
|
12
16
|
function zipUpTemplate(template_dir, config) {
|
|
13
17
|
return new Promise(function(resolve, reject) {
|
|
@@ -48,6 +52,21 @@ function zipUpTemplate(template_dir, config) {
|
|
|
48
52
|
if (config.settings) zip.append(JSON.stringify(config.settings), { name: "settings.js" });
|
|
49
53
|
if (config.data) zip.append(JSON.stringify(config.data), { name: "data.json" });
|
|
50
54
|
|
|
55
|
+
const data_dir = path.join(template_dir, "data");
|
|
56
|
+
if (fs.existsSync(data_dir)) {
|
|
57
|
+
// FIXME: check inferred types are compatible with specified types of data bindings
|
|
58
|
+
const column_types_by_sheet = {};
|
|
59
|
+
const files = fs.readdirSync(data_dir).filter(d => d.endsWith(".csv"));
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
let csv_text = fs.readFileSync(path.join(template_dir, "data", file), "utf8");
|
|
62
|
+
if (csv_text.charAt(0) === "\uFEFF") csv_text = csv_text.substr(1);
|
|
63
|
+
const parsed_csv = d3_dsv.csvParseRows(csv_text);
|
|
64
|
+
column_types_by_sheet[file.replace(/\.csv$/, "")] = data_utils.getColumnTypesForData(parsed_csv);
|
|
65
|
+
}
|
|
66
|
+
zip.append(JSON.stringify(column_types_by_sheet), { name: "column_types_by_sheet.json" });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
51
70
|
if (fs.existsSync(path.join(template_dir, "GUIDE.md"))) {
|
|
52
71
|
let file_path = path.join(template_dir, "GUIDE.md");
|
|
53
72
|
zip.file(file_path, { name: "README.md" });
|
|
@@ -77,17 +96,16 @@ function zipUpTemplate(template_dir, config) {
|
|
|
77
96
|
}
|
|
78
97
|
|
|
79
98
|
function uploadTemplate(server_opts, template_id, external_version, zip_filename) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
contentType: "application/zip",
|
|
88
|
-
}
|
|
89
|
-
}
|
|
99
|
+
const body = new FormData();
|
|
100
|
+
|
|
101
|
+
body.append("id", template_id);
|
|
102
|
+
body.append("version", external_version);
|
|
103
|
+
body.append("template", fs.createReadStream(zip_filename), {
|
|
104
|
+
contentType: "application/zip",
|
|
105
|
+
filename: "template.zip"
|
|
90
106
|
});
|
|
107
|
+
|
|
108
|
+
return sdk.request(server_opts, "template/publish", body);
|
|
91
109
|
}
|
|
92
110
|
|
|
93
111
|
function publish(args, server_opts) {
|
|
@@ -100,7 +118,7 @@ function publish(args, server_opts) {
|
|
|
100
118
|
if (args.release) return sdk.removePrereleaseTag(template_dir);
|
|
101
119
|
})
|
|
102
120
|
.then(() => sdk.readAndValidateConfig(template_dir))
|
|
103
|
-
.then((config) => {
|
|
121
|
+
.then(({config, warnings}) => {
|
|
104
122
|
if (!config.id) log.die("The template’s template.yml doesn’t have an id. Add one and try again.");
|
|
105
123
|
|
|
106
124
|
if (config.id.indexOf("/") > -1) {
|
|
@@ -143,6 +161,7 @@ function publish(args, server_opts) {
|
|
|
143
161
|
log.victory(`Uploaded version ${external_version} on ${dt.toDateString()} at ${dt.toTimeString()}`,
|
|
144
162
|
`Your template is available at ${protocol}://${server_opts.host}/@${template_path}/${external_version}`);
|
|
145
163
|
}
|
|
164
|
+
warnings.forEach(warning => log.warn(warning));
|
|
146
165
|
});
|
|
147
166
|
})
|
|
148
167
|
.catch((error) => {
|
package/lib/cmd/run.js
CHANGED
package/lib/cmd/version.js
CHANGED
|
@@ -4,7 +4,7 @@ var log = require("../log"),
|
|
|
4
4
|
sdk = require("../sdk");
|
|
5
5
|
|
|
6
6
|
function version(args) {
|
|
7
|
-
sdk.
|
|
7
|
+
sdk.getSdkVersion()
|
|
8
8
|
.then((version_number) => console.log(version_number))
|
|
9
9
|
.catch((error) => {
|
|
10
10
|
if (args.debug) log.die("Failed to get SDK version number", error.message, error.stack);
|
package/lib/log.js
CHANGED
|
@@ -12,7 +12,7 @@ function info(...lines) {
|
|
|
12
12
|
for (let line of lines) console.log(colors.yellow(" " + line));
|
|
13
13
|
}
|
|
14
14
|
function warn(...lines) {
|
|
15
|
-
for (let line of lines) console.warn(colors.yellow(" " + line));
|
|
15
|
+
for (let line of lines) console.warn(colors.yellow("⚠️ " + line));
|
|
16
16
|
}
|
|
17
17
|
function warn_bold(...lines) {
|
|
18
18
|
for (let line of lines) console.warn(colors.bold.yellow(" " + line));
|
package/lib/sdk.js
CHANGED
|
@@ -4,7 +4,8 @@ const fs = require("fs"),
|
|
|
4
4
|
path = require("path"),
|
|
5
5
|
|
|
6
6
|
cross_spawn = require("cross-spawn"),
|
|
7
|
-
|
|
7
|
+
fetch = require("node-fetch"),
|
|
8
|
+
FormData = require("form-data"),
|
|
8
9
|
shell_quote = require("shell-quote"),
|
|
9
10
|
yaml = require("js-yaml"),
|
|
10
11
|
nodeResolve = require("resolve"),
|
|
@@ -21,7 +22,7 @@ const YAML_DUMP_OPTS = { flowLevel: 4 };
|
|
|
21
22
|
|
|
22
23
|
const package_json_filename = path.join(__dirname, "..", "package.json");
|
|
23
24
|
var sdk_version = null;
|
|
24
|
-
function
|
|
25
|
+
function getSdkVersion() {
|
|
25
26
|
if (sdk_version) return Promise.resolve(sdk_version);
|
|
26
27
|
return new Promise(function(resolve, reject) {
|
|
27
28
|
fs.readFile(package_json_filename, "utf8", function(error, package_json) {
|
|
@@ -33,7 +34,7 @@ function getSDKVersion() {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function getSDKMajorVersion() {
|
|
36
|
-
return
|
|
37
|
+
return getSdkVersion()
|
|
37
38
|
.then((sdk_version) => {
|
|
38
39
|
const version_tuple = sdk_version.split(".").map((x) => parseInt(x));
|
|
39
40
|
return version_tuple[0];
|
|
@@ -94,77 +95,83 @@ const AUTHENTICATED_REQUEST_METHODS = new Set([
|
|
|
94
95
|
"user/whoami"
|
|
95
96
|
]);
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
]);
|
|
98
|
+
async function request(server_opts, method, data) {
|
|
99
|
+
let sdk_token;
|
|
100
100
|
|
|
101
|
-
function request(server_opts, method, data) {
|
|
102
|
-
let read_sdk_token_if_necessary;
|
|
103
101
|
if (AUTHENTICATED_REQUEST_METHODS.has(method)) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
read_sdk_token_if_necessary = Promise.resolve();
|
|
102
|
+
try {
|
|
103
|
+
sdk_token = await getSdkToken(server_opts);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
log.problem(`Failed to read ${sdk_tokens_file}`, error.message);
|
|
107
|
+
}
|
|
108
|
+
if (!sdk_token) {
|
|
109
|
+
log.die("You are not logged in. Try ‘flourish login’ or ‘flourish register’ first.");
|
|
110
|
+
}
|
|
117
111
|
}
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
protocol = "http";
|
|
124
|
-
}
|
|
125
|
-
let url = protocol + "://" + server_opts.host + "/api/v1/" + method;
|
|
126
|
-
let request_params = {
|
|
127
|
-
method: "POST",
|
|
128
|
-
uri: url,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
Object.assign(data, { sdk_token, sdk_version });
|
|
132
|
-
if (server_opts.user) {
|
|
133
|
-
request_params.auth = {
|
|
134
|
-
user: server_opts.user,
|
|
135
|
-
pass: server_opts.password,
|
|
136
|
-
sendImmediately: true,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
113
|
+
const sdk_version = await getSdkVersion();
|
|
114
|
+
const protocol = server_opts.host.match(/^(localhost|127\.0\.0\.1|.*\.local)(:\d+)?$/) ? "http:" : "https:";
|
|
115
|
+
const url = `${protocol}//${server_opts.host}/api/v1/${method}`;
|
|
116
|
+
const options = { method: data ? "POST" : "GET" };
|
|
139
117
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
request_params.headers = { "Content-Type": "application/json" };
|
|
145
|
-
request_params.body = JSON.stringify(data);
|
|
118
|
+
if (data) {
|
|
119
|
+
if (data instanceof FormData) {
|
|
120
|
+
if (sdk_token) {
|
|
121
|
+
data.append("sdk_token", sdk_token);
|
|
146
122
|
}
|
|
123
|
+
data.append("sdk_version", sdk_version);
|
|
124
|
+
options.headers = data.getHeaders();
|
|
125
|
+
options.body = data;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
options.body = JSON.stringify({ ...data, sdk_token, sdk_version });
|
|
129
|
+
options.headers = { "content-type": "application/json" };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
147
132
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
let r;
|
|
152
|
-
try { r = JSON.parse(res.body); }
|
|
153
|
-
catch (error) {
|
|
154
|
-
log.die("Failed to parse response from server", error, res.body);
|
|
155
|
-
}
|
|
156
|
-
return resolve(r);
|
|
157
|
-
}
|
|
133
|
+
if (server_opts.user) {
|
|
134
|
+
options.headers.authorization = Buffer.from(`${server_opts.user}:${server_opts.password}`).toString("base64");
|
|
135
|
+
}
|
|
158
136
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
137
|
+
let res;
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
res = await fetch(url, options);
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
log.die(e);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let text;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// We could use res.json() here, but we're interested in what the body
|
|
150
|
+
// is when it's *not* json (load balancer issues etc.).
|
|
151
|
+
text = await res.text();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
log.die("Failed to get response from server", error);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let body;
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
body = JSON.parse(text);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
log.die("Failed to parse response body", res.status, error, text);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (res.ok) {
|
|
167
|
+
return body;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (body.error) {
|
|
171
|
+
log.die("Error from server", res.status, body.error);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
log.die("Server error", res.status, body);
|
|
168
175
|
}
|
|
169
176
|
|
|
170
177
|
function runBuildCommand(template_dir, command, node_env) {
|
|
@@ -346,13 +353,16 @@ async function resolveImports(config, template_dir) {
|
|
|
346
353
|
for (let name in override) {
|
|
347
354
|
if (name === "property" || name === "method") continue;
|
|
348
355
|
if (method === "extend") {
|
|
356
|
+
if (["show_if", "hide_if"].includes(name) && typeof override[name] === "boolean") {
|
|
357
|
+
throw new Error(`Cannot extend a ${name} with Boolean value for property ${s.property}`);
|
|
358
|
+
}
|
|
349
359
|
let extendee = s[name];
|
|
350
360
|
if (extendee === undefined) {
|
|
351
361
|
if (name === "show_if" && s.hide_if !== undefined) {
|
|
352
|
-
Error(`Cannot extend a show_if when hide_if defined for property ${s.property}`);
|
|
362
|
+
throw new Error(`Cannot extend a show_if when hide_if defined for property ${s.property}`);
|
|
353
363
|
}
|
|
354
364
|
else if (name === "hide_if" && s.show_if !== undefined) {
|
|
355
|
-
Error(`Cannot extend a hide_if when show_if defined for property ${s.property}`);
|
|
365
|
+
throw new Error(`Cannot extend a hide_if when show_if defined for property ${s.property}`);
|
|
356
366
|
}
|
|
357
367
|
extendee = {};
|
|
358
368
|
}
|
|
@@ -380,6 +390,26 @@ async function resolveImports(config, template_dir) {
|
|
|
380
390
|
return config;
|
|
381
391
|
}
|
|
382
392
|
|
|
393
|
+
// Sets a default binding data_type of string in templates with both typed and untyped bindings, and return a post-publish warning message.
|
|
394
|
+
function checkDataTypes(config) {
|
|
395
|
+
const warnings = [];
|
|
396
|
+
if (!config.data) return { config, warnings };
|
|
397
|
+
|
|
398
|
+
const all_bindings = config.data.filter(binding => typeof binding !== "string"); // filter out title and description strings
|
|
399
|
+
const any_bindings_are_typed = all_bindings.some(binding => binding.data_type);
|
|
400
|
+
|
|
401
|
+
if (any_bindings_are_typed) {
|
|
402
|
+
config.data.forEach(binding => {
|
|
403
|
+
if (typeof binding === "string") return;
|
|
404
|
+
if (!binding.data_type) {
|
|
405
|
+
binding.data_type = "string";
|
|
406
|
+
warnings.push(`Missing data_type for key ${binding.key} in dataset ${binding.dataset} - assuming "string"`);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return { config, warnings };
|
|
411
|
+
}
|
|
412
|
+
|
|
383
413
|
function readAndValidateConfig(template_dir) {
|
|
384
414
|
return readConfig(template_dir)
|
|
385
415
|
.then((config) => {
|
|
@@ -387,7 +417,8 @@ function readAndValidateConfig(template_dir) {
|
|
|
387
417
|
return config;
|
|
388
418
|
})
|
|
389
419
|
.then(config => addShowConditions(config))
|
|
390
|
-
.then(config => resolveImports(config, template_dir))
|
|
420
|
+
.then(config => resolveImports(config, template_dir))
|
|
421
|
+
.then(config => checkDataTypes(config));
|
|
391
422
|
}
|
|
392
423
|
|
|
393
424
|
function changeVersionNumberInPackageJson(template_dir, change_function) {
|
|
@@ -528,7 +559,7 @@ const TEMPLATE_SPECIAL = new Set([
|
|
|
528
559
|
]);
|
|
529
560
|
|
|
530
561
|
module.exports = {
|
|
531
|
-
checkTemplateVersion,
|
|
562
|
+
checkTemplateVersion, getSdkVersion, getSDKMajorVersion,
|
|
532
563
|
|
|
533
564
|
getSdkToken, setSdkToken, deleteSdkTokens,
|
|
534
565
|
request,
|
package/lib/validate_config.js
CHANGED
|
@@ -227,7 +227,8 @@ function validateSetting(template_directory, conditional_settings, setting) {
|
|
|
227
227
|
throw new Error(`template.yml setting “${property}” has unsupported width property: must be “full”, “half”, “quarter” or “three quarters”`);
|
|
228
228
|
}
|
|
229
229
|
if ("size" in setting) {
|
|
230
|
-
|
|
230
|
+
const can_set_size = setting.type == "code" || setting.type == "text" || (setting.type == "string" && setting.style == "buttons");
|
|
231
|
+
if (!can_set_size) throw new Error(`template.yml setting “${property}” has a “size” property; this requires type “text” or “code”, or type “string” with ”style: buttons”`);
|
|
231
232
|
else if (setting.size !== "large") throw new Error(`template.yml setting “${property}” has unsupported size property: must be “large”`);
|
|
232
233
|
}
|
|
233
234
|
}
|
|
@@ -253,8 +254,8 @@ function validateSettings(template_directory, settings, bindings) {
|
|
|
253
254
|
if (conditional_settings.size > 0) {
|
|
254
255
|
conditional_settings.forEach(function(conditional_setting) {
|
|
255
256
|
if (/^data\./.test(conditional_setting)) {
|
|
256
|
-
if (!/^data\.\w+\.\w
|
|
257
|
-
throw new Error(`template.yml: “show_if” or “hide_if” property specifies invalid data binding “${conditional_setting}”`);
|
|
257
|
+
if (!/^data\.\w+\.\w+(\.type)?$/.test(conditional_setting)) {
|
|
258
|
+
throw new Error(`template.yml: “show_if” or “hide_if” property specifies invalid data binding or column type “${conditional_setting}”`);
|
|
258
259
|
}
|
|
259
260
|
if (!bindings || !Array.isArray(bindings)) {
|
|
260
261
|
throw new Error(`template.yml: “show_if” or “hide_if” property refers to data binding “${conditional_setting}” when none are defined`);
|
|
@@ -271,7 +272,7 @@ function validateSettings(template_directory, settings, bindings) {
|
|
|
271
272
|
}
|
|
272
273
|
}
|
|
273
274
|
|
|
274
|
-
function validateColSpec(spec, parser, data_table_names) {
|
|
275
|
+
function validateColSpec(spec, parser, data_table_names, is_optional) {
|
|
275
276
|
const double_colon_ix = spec.indexOf("::");
|
|
276
277
|
if (double_colon_ix == -1) throw new Error("Invalid data binding: " + spec);
|
|
277
278
|
const data_table_name = spec.substr(0, double_colon_ix);
|
|
@@ -280,7 +281,7 @@ function validateColSpec(spec, parser, data_table_names) {
|
|
|
280
281
|
}
|
|
281
282
|
|
|
282
283
|
const col_spec = spec.substr(double_colon_ix + 2);
|
|
283
|
-
parser(col_spec);
|
|
284
|
+
parser(col_spec, is_optional);
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
const VALID_DATA_BINDING_TYPES = new Set(["column", "columns"]);
|
|
@@ -320,7 +321,7 @@ function validateDataBinding(binding, data_table_names) {
|
|
|
320
321
|
if (typeof binding.column !== "string") {
|
|
321
322
|
throw new Error(`template.yml: “column” property of data binding “${binding_name}” must be a string`);
|
|
322
323
|
}
|
|
323
|
-
validateColSpec(binding.column, columns.parseColumn, data_table_names);
|
|
324
|
+
validateColSpec(binding.column, columns.parseColumn, data_table_names, binding.optional);
|
|
324
325
|
}
|
|
325
326
|
}
|
|
326
327
|
else if (binding.type == "columns") {
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flourish/sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"description": "The Flourish SDK",
|
|
5
5
|
"module": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prepare": "cd .. && make sdk_clean sdk",
|
|
8
|
-
"test": "mocha"
|
|
8
|
+
"test": "mocha --recursive",
|
|
9
|
+
"audit": "check-audit",
|
|
10
|
+
"audit:resolve": "resolve-audit"
|
|
9
11
|
},
|
|
10
12
|
"bin": {
|
|
11
13
|
"flourish": "bin/flourish"
|
|
@@ -14,34 +16,40 @@
|
|
|
14
16
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
15
17
|
"repository": "kiln/flourish-sdk",
|
|
16
18
|
"dependencies": {
|
|
19
|
+
"@flourish/interpreter": "^6.0.3",
|
|
17
20
|
"@flourish/semver": "^1.0.1",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"@flourish/transform-data": "^2.1.0",
|
|
22
|
+
"@handlebars/allow-prototype-access": "^1.0.3",
|
|
23
|
+
"@rollup/plugin-commonjs": "^17.1.0",
|
|
24
|
+
"archiver": "^5.0.2",
|
|
25
|
+
"chokidar": "^3.4.3",
|
|
26
|
+
"colors": "^1.4.0",
|
|
27
|
+
"cross-spawn": "^7.0.3",
|
|
28
|
+
"d3-dsv": "^2.0.0",
|
|
23
29
|
"express": "^4.17.1",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
30
|
+
"form-data": "^4.0.0",
|
|
31
|
+
"handlebars": "^4.7.6",
|
|
32
|
+
"js-yaml": "^3.14.0",
|
|
33
|
+
"minimist": "^1.2.5",
|
|
27
34
|
"ncp": "^2.0.0",
|
|
28
|
-
"
|
|
35
|
+
"node-fetch": "^2.6.6",
|
|
36
|
+
"parse5": "^6.0.1",
|
|
29
37
|
"read": "^1.0.7",
|
|
30
|
-
"
|
|
31
|
-
"resolve": "^1.11.1",
|
|
38
|
+
"resolve": "^1.18.1",
|
|
32
39
|
"rewrite-links": "^1.1.0",
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
40
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
41
|
+
"shell-quote": "^1.7.2",
|
|
42
|
+
"tmp": "^0.2.1",
|
|
43
|
+
"ws": "^7.4.6"
|
|
36
44
|
},
|
|
37
45
|
"devDependencies": {
|
|
46
|
+
"@rollup/plugin-node-resolve": "^9.0.0",
|
|
38
47
|
"d3-request": "^1.0.6",
|
|
39
|
-
"mocha": "^
|
|
40
|
-
"
|
|
41
|
-
"rollup
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"tempy": "^0.3.0"
|
|
48
|
+
"mocha": "^9.1.2",
|
|
49
|
+
"npm-audit-resolver": "^2.3.0",
|
|
50
|
+
"rollup": "^2.32.1",
|
|
51
|
+
"sinon": "^9.2.0",
|
|
52
|
+
"tempy": "^1.0.0"
|
|
45
53
|
},
|
|
46
54
|
"engines": {
|
|
47
55
|
"node": ">=8.3"
|