CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/opts.js
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
//##############################################################################
7
//
8
// CoCalc: Collaborative web-based calculation
9
// Copyright (C) 2017, Sagemath Inc.
10
// MS-RSL
11
//
12
//##############################################################################
13
14
/*
15
Handling of input opts to functions and type checking.
16
*/
17
18
let DEBUG, TEST_MODE, val;
19
const PropTypes = require("prop-types");
20
21
const immutable_types = require("./immutable-types");
22
23
/*
24
Testing related env/DEVEL/DEBUG stuff
25
*/
26
27
if (
28
__guard__(
29
typeof process !== "undefined" && process !== null
30
? process.env
31
: undefined,
32
(x) => x.DEVEL,
33
) &&
34
!__guard__(
35
typeof process !== "undefined" && process !== null
36
? process.env
37
: undefined,
38
(x1) => x1.SMC_TEST,
39
)
40
) {
41
// Running on node and DEVEL is set and not running under test suite
42
DEBUG = true;
43
} else {
44
DEBUG = false;
45
}
46
47
// console.debug only logs if DEBUG is true
48
if (DEBUG) {
49
console.debug = console.log;
50
} else {
51
console.debug = function () {};
52
}
53
54
if (
55
__guard__(
56
typeof process !== "undefined" && process !== null
57
? process.env
58
: undefined,
59
(x2) => x2.SMC_TEST,
60
)
61
) {
62
// in test mode we *do* want exception to get thrown below when type checks fails
63
TEST_MODE = true;
64
}
65
66
// Checks property types on a target object with checkers in a declaration.
67
// Declarations should throw an Error for mismatches and undefined if OK.
68
const types = (exports.types = function (
69
target,
70
declaration,
71
identifier = "check.types",
72
) {
73
if (identifier == null) {
74
identifier = "check.types";
75
}
76
if (typeof target !== "object") {
77
throw new Error("Types was given a non-object to check");
78
}
79
80
if (typeof declaration !== "object") {
81
throw new Error(
82
`Types was given a ${typeof declaration} as a declaration instead of an object`,
83
);
84
}
85
86
return PropTypes.checkPropTypes(
87
declaration,
88
target,
89
"checking a",
90
identifier,
91
);
92
});
93
94
for (let key in PropTypes) {
95
val = PropTypes[key];
96
if (key !== "checkPropTypes" && key !== "PropTypes") {
97
types[key] = val;
98
}
99
}
100
101
types.immutable = immutable_types.immutable;
102
103
// Returns a new object with properties determined by those of obj1 and
104
// obj2. The properties in obj1 *must* all also appear in obj2. If an
105
// obj2 property has value "defaults.required", then it must appear in
106
// obj1. For each property P of obj2 not specified in obj1, the
107
// corresponding value obj1[P] is set (all in a new copy of obj1) to
108
// be obj2[P].
109
exports.defaults = function (obj1, obj2, allow_extra = false, strict = false) {
110
let err;
111
if (strict == null) {
112
strict = false;
113
}
114
if (obj1 == null) {
115
obj1 = {};
116
}
117
const error = function () {
118
try {
119
return `(obj1=${exports.trunc(
120
exports.to_json(obj1),
121
1024,
122
)}, obj2=${exports.trunc(exports.to_json(obj2), 1024)})`;
123
} catch (err) {
124
return "";
125
}
126
};
127
if (obj1 == null) {
128
// useful special case
129
obj1 = {};
130
}
131
if (typeof obj1 !== "object") {
132
// We put explicit traces before the errors in this function,
133
// since otherwise they can be very hard to debug.
134
err = `BUG -- Traceback -- misc.defaults -- TypeError: function takes inputs as an object ${error()}`;
135
if (strict || DEBUG || TEST_MODE) {
136
throw new Error(err);
137
} else {
138
console.log(err);
139
console.trace();
140
return obj2;
141
}
142
}
143
const r = {};
144
for (var prop in obj2) {
145
val = obj2[prop];
146
if (obj1.hasOwnProperty(prop) && obj1[prop] != null) {
147
if (obj2[prop] === exports.defaults.required && obj1[prop] == null) {
148
err = `misc.defaults -- TypeError: property '${prop}' must be specified: ${error()}`;
149
if (strict || DEBUG || TEST_MODE) {
150
throw new Error(err);
151
} else {
152
console.warn(err);
153
console.trace();
154
}
155
}
156
r[prop] = obj1[prop];
157
} else if (obj2[prop] != null) {
158
// only record not undefined properties
159
if (obj2[prop] === exports.defaults.required) {
160
err = `misc.defaults -- TypeError: property '${prop}' must be specified: ${error()}`;
161
if (strict || DEBUG || TEST_MODE) {
162
throw new Error(err);
163
} else {
164
console.warn(err);
165
console.trace();
166
}
167
} else {
168
r[prop] = obj2[prop];
169
}
170
}
171
}
172
if (!allow_extra) {
173
for (prop in obj1) {
174
val = obj1[prop];
175
if (!obj2.hasOwnProperty(prop)) {
176
err = `misc.defaults -- TypeError: got an unexpected argument '${prop}' ${error()}`;
177
console.trace();
178
if (strict || DEBUG || TEST_MODE) {
179
throw new Error(err);
180
} else {
181
console.warn(err);
182
}
183
}
184
}
185
}
186
return r;
187
};
188
189
// WARNING -- don't accidentally use this as a default:
190
const required =
191
(exports.required =
192
exports.defaults.required =
193
"__!!!!!!this is a required property!!!!!!__");
194
195
function __guard__(value, transform) {
196
return typeof value !== "undefined" && value !== null
197
? transform(value)
198
: undefined;
199
}
200
201