@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
|
@@ -1,67 +1,237 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { setProp } = require('../base/model');
|
|
4
4
|
const {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
forEachDefinition,
|
|
6
|
+
forAllQueries,
|
|
7
7
|
getUtils,
|
|
8
|
-
|
|
8
|
+
forEachMember,
|
|
9
|
+
forEachMemberRecursively,
|
|
9
10
|
} = require('../model/csnUtils');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* Loop through a universal CSN and enrich it with the properties
|
|
13
|
+
* Loop through a universal CSN and enrich it with the properties/annotations
|
|
13
14
|
* from the source definition - modifies the input model in-place
|
|
14
|
-
*
|
|
15
|
+
*
|
|
15
16
|
* @param {CSN.Model} csn
|
|
16
17
|
* @param {CSN.Options} options
|
|
17
18
|
*/
|
|
18
|
-
module.exports = function(csn, options) {
|
|
19
|
-
let { getOrigin,
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
module.exports = function (csn, options) {
|
|
20
|
+
let { getOrigin, getQueryPrimarySource, artifactRef } = getUtils(csn);
|
|
21
|
+
|
|
22
|
+
// Properties on definition level that we treat specially.
|
|
23
|
+
// TODO: There might be more annotations that will need special treatment
|
|
24
|
+
// see lib/compiler/propagator.js for reference
|
|
25
|
+
const defProps = {
|
|
26
|
+
'@cds.autoexpose': onlyViaArtifact,
|
|
27
|
+
'@fiori.draft.enabled': onlyViaArtifact,
|
|
28
|
+
'@': (prop, target, source) => { target[prop] = source[prop] },
|
|
29
|
+
// Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
|
|
30
|
+
'elements': onlyTypeDef,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// In this first loop through the model, missing properties in universal CSN
|
|
34
|
+
// are propagated so the CSN can become client one
|
|
35
|
+
forEachDefinition(csn, propagate);
|
|
36
|
+
|
|
37
|
+
// The $origin properties need to be removed separately
|
|
38
|
+
// as the values are used in csnRef::getOrigin that is used during
|
|
39
|
+
// the propagation above.
|
|
40
|
+
// Currently testMode-only for comparison against client CSN.
|
|
41
|
+
if (options.testMode) removeDollarProperties(csn);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Identify the sources of the passed object and propagate the relevant
|
|
45
|
+
* properties/annotations.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} art Target object for propagation
|
|
48
|
+
*/
|
|
49
|
+
function propagate(art) {
|
|
50
|
+
// check if art was already processed by the status flag
|
|
51
|
+
// TODO: clean up later on, together with validator clean up probably or
|
|
52
|
+
// when this module is meant to be used standalone -> use internal cache to store already processed definitions?
|
|
53
|
+
if (art._status === 'propagated') return;
|
|
54
|
+
|
|
55
|
+
// collect chain of origins and propagate
|
|
56
|
+
// from the farthest to the nearest one to the target
|
|
57
|
+
let chain = [];
|
|
58
|
+
let target = art;
|
|
59
|
+
let origin = undefined;
|
|
60
|
+
do {
|
|
61
|
+
origin = getOrigin(target);
|
|
62
|
+
if (origin) {
|
|
63
|
+
chain.push({ target, origin });
|
|
64
|
+
target = origin;
|
|
65
|
+
}
|
|
66
|
+
} while (origin);
|
|
67
|
+
|
|
68
|
+
if (chain.length)
|
|
69
|
+
chain.reverse().forEach(propagateSingleStep);
|
|
70
|
+
else
|
|
71
|
+
// even if there weren't any found origin(s) on definition level
|
|
72
|
+
// we need to loop through the members
|
|
73
|
+
// TODO: construct use-/test-case where there might be the need that an origin chain
|
|
74
|
+
// needs to be constructed for members as well, specifically for this 'else'
|
|
75
|
+
// case where we do not run through the definitions
|
|
76
|
+
propagateMembersProps(target);
|
|
77
|
+
|
|
78
|
+
function propagateSingleStep({ target, origin }) {
|
|
79
|
+
// if target was already processed -> continue
|
|
80
|
+
if (target._status === 'propagated') return;
|
|
81
|
+
// propagate relevant definition level properties
|
|
82
|
+
// we check for kind as in the future the function should be
|
|
83
|
+
// generic and work for parts of CSN
|
|
84
|
+
if (target.kind) propagateDefProps(target, origin);
|
|
85
|
+
// propagate properties to members
|
|
86
|
+
propagateMembersProps(target);
|
|
87
|
+
|
|
88
|
+
setProp(target, '_status', 'propagated');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Propagate from 'source' to 'target' the relevant properties
|
|
93
|
+
* for CSN definitions.
|
|
94
|
+
*
|
|
95
|
+
* @param {CSN.Definition} target
|
|
96
|
+
* @param {CSN.Definition} source
|
|
97
|
+
*/
|
|
98
|
+
function propagateDefProps(target, source) {
|
|
99
|
+
const keys = Object.keys(source);
|
|
100
|
+
for (const prop of keys) {
|
|
101
|
+
// do not overwrite properties in target def
|
|
102
|
+
if (!(prop in target)) {
|
|
103
|
+
const func = defProps[prop] || defProps[prop.charAt(0)];
|
|
104
|
+
if (func) func(prop, target, source);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Propagate properties from Universal to Client CSN relevant for members.
|
|
111
|
+
*
|
|
112
|
+
* @param {CSN.Artifact} target
|
|
113
|
+
*/
|
|
114
|
+
function propagateMembersProps(target) {
|
|
115
|
+
// TODO: in the future, consider case when target is a member itself,
|
|
116
|
+
// for when we do not run with the complete CSN
|
|
117
|
+
// TODO: use newly added 'forEachMemberRecursivelyWithQuery'
|
|
118
|
+
forEachMemberRecursively(target, (member) => {
|
|
119
|
+
propagateMemberPropsFromOrigin(member);
|
|
120
|
+
if (member.target && !member.keys && !member.on)
|
|
121
|
+
calculateForeignKeys(member);
|
|
122
|
+
});
|
|
123
|
+
target.query && forAllQueries(target.query, (query) => {
|
|
124
|
+
if (query.SELECT && query.SELECT.elements) {
|
|
125
|
+
forEachMember(query.SELECT, (member) => {
|
|
126
|
+
propagateMemberPropsFromOrigin(member);
|
|
127
|
+
if (member.target && !member.keys && !member.on)
|
|
128
|
+
calculateForeignKeys(member);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
setProp(target, '_status', 'propagated');
|
|
30
133
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
134
|
+
|
|
135
|
+
function propagateMemberPropsFromOrigin(member) {
|
|
136
|
+
// For empty members (`{}`), the origin was set in a previous call to `getOrigin(definition)`.
|
|
137
|
+
const memberOrigin = getOrigin(member);
|
|
138
|
+
if (!memberOrigin) return;
|
|
139
|
+
|
|
140
|
+
// when having an element with a type property that is
|
|
141
|
+
// user-defined there is no need to propagate 'elements',
|
|
142
|
+
// 'kind', etc. from the origin (which is the type definition)
|
|
143
|
+
if (member.type && Object.keys(member).length === 1) return;
|
|
144
|
+
|
|
145
|
+
const keys = Object.keys(memberOrigin);
|
|
146
|
+
// Copy over properties from the origin element.
|
|
147
|
+
// Don't propagate "kind" as this property is not allowed in elements.
|
|
148
|
+
for (const key of keys) {
|
|
149
|
+
if (!(key in member) && key !== 'kind')
|
|
150
|
+
member[key] = memberOrigin[key];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// copy over own annotations/properties
|
|
154
|
+
// TODO: try to create use-/test-case where this needs
|
|
155
|
+
// to be applied on definition level, ATM it is done only for members
|
|
156
|
+
if (member.$origin && !Array.isArray(member.$origin) && member.$origin.$origin) {
|
|
157
|
+
const ownKeys = Object.keys(member.$origin);
|
|
158
|
+
for (const key of ownKeys) {
|
|
159
|
+
if (key !== '$origin')
|
|
160
|
+
member[key] = member.$origin[key];
|
|
43
161
|
}
|
|
162
|
+
}
|
|
44
163
|
|
|
164
|
+
// In case of managed composition an anonymous $origin is used.
|
|
165
|
+
// csnRefs::getOrigin returns {} for such a member, thus have to recreate the client CSN from
|
|
166
|
+
// the values in the $origin. PR #8072
|
|
167
|
+
if (!Object.keys(memberOrigin).length && member.$origin && member.$origin.type === 'cds.Composition') {
|
|
168
|
+
member.type = member.$origin.type;
|
|
169
|
+
member.cardinality = member.$origin.cardinality;
|
|
170
|
+
member.targetAspect = member.$origin.target;
|
|
45
171
|
}
|
|
46
172
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
173
|
+
|
|
174
|
+
function calculateForeignKeys(member) {
|
|
175
|
+
// managed assocs in universal CSN have no longer keys
|
|
176
|
+
// if they are not explicitly defined - PR#8064
|
|
177
|
+
const target = artifactRef(member.target);
|
|
178
|
+
const targetKeys = Object.keys(target.elements).filter((key) => target.elements[key].key);
|
|
179
|
+
member.keys = targetKeys.map(
|
|
180
|
+
keyName => { return { ref: [keyName] } }
|
|
181
|
+
);
|
|
57
182
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @cds.autoexpose for example, is propagated only if at definition level and only if
|
|
188
|
+
* the primary source (left-most) does not follow an association.
|
|
189
|
+
*
|
|
190
|
+
* @param {String} prop
|
|
191
|
+
* @param {CSN.Definition} target
|
|
192
|
+
* @param {CSN.Definition} source
|
|
193
|
+
*/
|
|
194
|
+
function onlyViaArtifact(prop, target, source) {
|
|
195
|
+
if (!target.kind) return;
|
|
196
|
+
const primarySourceRef = getQueryPrimarySource(target.query || target.projection);
|
|
197
|
+
const artRef = primarySourceRef ? artifactRef(primarySourceRef) : source;
|
|
198
|
+
if (!artRef.target) {
|
|
199
|
+
target[prop] = source[prop];
|
|
63
200
|
}
|
|
64
|
-
|
|
65
|
-
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Execute only if the target definition is a user-defined type.
|
|
205
|
+
*
|
|
206
|
+
* @param {String} prop
|
|
207
|
+
* @param {CSN.Definition} target
|
|
208
|
+
* @param {CSN.Definition} source
|
|
209
|
+
* @returns
|
|
210
|
+
*/
|
|
211
|
+
function onlyTypeDef(prop, target, source) {
|
|
212
|
+
if (target.kind !== 'type') return;
|
|
213
|
+
target[prop] = source[prop];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Removes every occurrence of '$origin' and '$generated'
|
|
218
|
+
* for compatibility with what we have in client CSN.
|
|
219
|
+
*
|
|
220
|
+
* @param {CSN.Model} csn
|
|
221
|
+
*/
|
|
222
|
+
function removeDollarProperties(csn) {
|
|
223
|
+
forEachDefinition(csn, (def) => {
|
|
224
|
+
delete def.$origin;
|
|
225
|
+
delete def.$generated;
|
|
226
|
+
// TODO: use newly added 'forEachMemberRecursivelyWithQuery'
|
|
227
|
+
forEachMemberRecursively(def, (member) => delete member.$origin);
|
|
228
|
+
def.query && forAllQueries(def.query, (query) => {
|
|
229
|
+
if (query.SELECT && query.SELECT.elements) {
|
|
230
|
+
forEachMember(query.SELECT, (member) => {
|
|
231
|
+
delete member.$origin;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
})
|
|
66
236
|
}
|
|
67
237
|
}
|
package/lib/utils/file.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
|
+
const util = require('util');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Split the given source string into its lines. Respects Unix,
|
|
9
|
-
* Windows
|
|
10
|
+
* Windows and Macintosh line breaks.
|
|
10
11
|
*
|
|
11
12
|
* @param {string} src
|
|
12
13
|
* @returns {string[]}
|
|
@@ -19,8 +20,8 @@ function splitLines(src) {
|
|
|
19
20
|
* Returns filesystem utils readFile(), isFile(), realpath() for _CDS_ usage.
|
|
20
21
|
* This includes a trace as well as usage of a file cache.
|
|
21
22
|
*
|
|
22
|
-
* Note: The synchronous versions accept a callback
|
|
23
|
-
* immediately! This is different from NodeJS's readFileSync()!
|
|
23
|
+
* Note: The synchronous versions accept a callback instead of being async (duh!), which
|
|
24
|
+
* is executed immediately! This is different from NodeJS's readFileSync()!
|
|
24
25
|
* This is done to allow using it in places where fs.readFile (async) is used.
|
|
25
26
|
*
|
|
26
27
|
* @param {object} fileCache
|
|
@@ -30,7 +31,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
30
31
|
const readFile = _wrapReadFileCached(fs.readFile);
|
|
31
32
|
const readFileSync = _wrapReadFileCached((filename, enc, cb) => {
|
|
32
33
|
try {
|
|
33
|
-
cb(null, fs.readFileSync( filename, enc ));
|
|
34
|
+
cb(null, fs.readFileSync( filename, { encoding: enc } ));
|
|
34
35
|
}
|
|
35
36
|
catch (err) {
|
|
36
37
|
cb(err, null);
|
|
@@ -47,10 +48,14 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
return {
|
|
51
|
+
/** @type {function(string, string)} */
|
|
52
|
+
readFileAsync: util.promisify(readFile),
|
|
50
53
|
readFile,
|
|
51
54
|
readFileSync,
|
|
55
|
+
isFileAsync: util.promisify(isFile),
|
|
52
56
|
isFile,
|
|
53
57
|
isFileSync,
|
|
58
|
+
realpathAsync: util.promisify(realpath),
|
|
54
59
|
realpath,
|
|
55
60
|
realpathSync,
|
|
56
61
|
};
|
|
@@ -72,7 +77,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
72
77
|
* Wraps the given reader into a cached environment including a trace.
|
|
73
78
|
* The given @p reader must have the same signature as fs.readFile.
|
|
74
79
|
*
|
|
75
|
-
* @param {(filename: string, enc
|
|
80
|
+
* @param {(filename: string, enc, cb: (err, data) => void) => void} reader
|
|
76
81
|
*/
|
|
77
82
|
function _wrapReadFileCached( reader ) {
|
|
78
83
|
return (filename, enc, cb) => {
|
|
@@ -93,7 +98,9 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
93
98
|
body.syscall = 'open';
|
|
94
99
|
body.path = filename;
|
|
95
100
|
}
|
|
96
|
-
if (body
|
|
101
|
+
if (body && body.stack && body.message) {
|
|
102
|
+
// NOTE: checks for instanceof Error are not reliable if error
|
|
103
|
+
// created in different execution env
|
|
97
104
|
traceFS( 'READFILE:cache-error:', filename, body.message );
|
|
98
105
|
cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
|
|
99
106
|
}
|
package/lib/utils/term.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// This file is used for color output to stderr and stdout.
|
|
3
3
|
// Use `term.error`, `term.warn` and `term.info` as they use color output
|
|
4
4
|
// per default if the process runs in a TTY, i.e. stdout as well as
|
|
5
|
-
// stderr are TTYs.
|
|
6
|
-
//
|
|
5
|
+
// stderr are TTYs. stderr/stdout are no TTYs if they are
|
|
6
|
+
// (for example) piped into another process or written to file:
|
|
7
7
|
//
|
|
8
8
|
// node myApp.js # stdout.isTTY: true, stderr.isTTY: true
|
|
9
9
|
// node myApp.js | cat # stdout.isTTY: undefined, stderr.isTTY: true
|
|
@@ -17,23 +17,9 @@
|
|
|
17
17
|
const stderrHasColor = process.stderr.isTTY;
|
|
18
18
|
const stdoutHasColor = process.stdout.isTTY;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
switch (mode) {
|
|
24
|
-
case false:
|
|
25
|
-
case 'never':
|
|
26
|
-
hasColor = false;
|
|
27
|
-
break;
|
|
28
|
-
case true:
|
|
29
|
-
case 'always':
|
|
30
|
-
hasColor = true;
|
|
31
|
-
break;
|
|
32
|
-
default:
|
|
33
|
-
hasColor = stdoutHasColor && stderrHasColor;
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
20
|
+
// Note: We require both stderr and stdout to be TTYs, as we don't
|
|
21
|
+
// know (in our exported functions) where the text will end up.
|
|
22
|
+
const hasColorShell = stdoutHasColor && stderrHasColor;
|
|
37
23
|
|
|
38
24
|
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
|
|
39
25
|
const t = {
|
|
@@ -47,29 +33,66 @@ const t = {
|
|
|
47
33
|
cyan: '\x1b[36m', // Foreground Cyan
|
|
48
34
|
};
|
|
49
35
|
|
|
50
|
-
|
|
36
|
+
function term(useColor = 'auto') {
|
|
37
|
+
let hasColor = hasColorShell;
|
|
38
|
+
changeColorMode(useColor);
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
function changeColorMode(mode) {
|
|
41
|
+
switch (mode) {
|
|
42
|
+
case false:
|
|
43
|
+
case 'never':
|
|
44
|
+
hasColor = false;
|
|
45
|
+
break;
|
|
46
|
+
case true:
|
|
47
|
+
case 'always':
|
|
48
|
+
hasColor = true;
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
// Note: See also: https://no-color.org/
|
|
52
|
+
// > Command-line software which adds ANSI color to its output by default
|
|
53
|
+
// > should check for the presence of a `NO_COLOR` environment variable
|
|
54
|
+
// > that, when present (regardless of its value), prevents the addition
|
|
55
|
+
// > of ANSI color.
|
|
56
|
+
// Note: To be able to disable colors in tests, we check the environment
|
|
57
|
+
// variable here again.
|
|
58
|
+
hasColor = hasColorShell && (process.env.NO_COLOR === undefined);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
67
61
|
}
|
|
68
|
-
};
|
|
69
62
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
const as = (codes, o) => (hasColor ? (codes + o + t.reset) : (`${ o }`));
|
|
64
|
+
|
|
65
|
+
const asError = o => as(t.red + t.bold, o);
|
|
66
|
+
const asWarning = o => as(t.yellow, o);
|
|
67
|
+
const asInfo = o => as(t.green, o);
|
|
68
|
+
const asHelp = o => as(t.cyan, o);
|
|
69
|
+
|
|
70
|
+
const underline = o => as(t.underline, o);
|
|
71
|
+
const bold = o => as(t.bold, o);
|
|
72
|
+
|
|
73
|
+
const asSeverity = (severity, msg) => {
|
|
74
|
+
switch ((`${ severity }`).toLowerCase()) {
|
|
75
|
+
case 'error': return asError(msg);
|
|
76
|
+
case 'warning': return asWarning(msg);
|
|
77
|
+
case 'info': return asInfo(msg);
|
|
78
|
+
case 'help': return asHelp(msg);
|
|
79
|
+
// or e.g. 'none'
|
|
80
|
+
default: return msg;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
changeColorMode,
|
|
86
|
+
as,
|
|
87
|
+
underline,
|
|
88
|
+
bold,
|
|
89
|
+
|
|
90
|
+
severity: asSeverity,
|
|
91
|
+
error: asError,
|
|
92
|
+
warn: asWarning,
|
|
93
|
+
info: asInfo,
|
|
94
|
+
help: asHelp,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { term };
|
package/lib/utils/timetrace.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* A single
|
|
4
|
+
* A single StopWatch encapsulates the runtime of a selected code frame.
|
|
5
5
|
*
|
|
6
6
|
* @class TimeTrace
|
|
7
7
|
*/
|
|
8
|
-
class
|
|
8
|
+
class StopWatch {
|
|
9
9
|
/**
|
|
10
10
|
* Creates an instance of TimeTrace.
|
|
11
11
|
* @param {string} id
|
|
@@ -13,29 +13,48 @@ class TimeTrace {
|
|
|
13
13
|
* @memberOf TimeTrace
|
|
14
14
|
*/
|
|
15
15
|
constructor(id) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.start = function start(indent) {
|
|
23
|
-
// eslint-disable-next-line no-console
|
|
24
|
-
console.error(`${ ' '.repeat((indent) * 2) }${ id } started`);
|
|
25
|
-
startTime = process.hrtime();
|
|
26
|
-
};
|
|
16
|
+
this.id = id;
|
|
17
|
+
// TODO: If we require Node 12, use process.hrtime.bigint()
|
|
18
|
+
// as process.hrtime() is deprecated.
|
|
19
|
+
// eslint-disable-next-line no-multi-assign
|
|
20
|
+
this.startTime = this.lapTime = process.hrtime();
|
|
21
|
+
}
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* @param {number} indent
|
|
23
|
+
/**
|
|
24
|
+
* Start watch.
|
|
32
25
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
start() {
|
|
27
|
+
// eslint-disable-next-line no-multi-assign
|
|
28
|
+
this.startTime = this.lapTime = process.hrtime();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Stop and return delta T
|
|
33
|
+
* but do not set start time
|
|
34
|
+
*/
|
|
35
|
+
stop() {
|
|
36
|
+
return process.hrtime(this.startTime);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* return lap time
|
|
41
|
+
*/
|
|
42
|
+
lap() {
|
|
43
|
+
const dt = process.hrtime(this.lapTime);
|
|
44
|
+
this.lapTime = process.hrtime();
|
|
45
|
+
return dt;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// stop as sec.ns float
|
|
49
|
+
stopInFloatSecs() {
|
|
50
|
+
const dt = this.stop();
|
|
51
|
+
return dt[0] + dt[1] / 1000000000;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// lap as sec.ns float
|
|
55
|
+
lapInFloatSecs() {
|
|
56
|
+
const dt = this.lap();
|
|
57
|
+
return dt[0] + dt[1] / 1000000000;
|
|
39
58
|
}
|
|
40
59
|
}
|
|
41
60
|
|
|
@@ -67,9 +86,11 @@ class TimeTracer {
|
|
|
67
86
|
*/
|
|
68
87
|
start(id) {
|
|
69
88
|
try {
|
|
70
|
-
const b = new
|
|
89
|
+
const b = new StopWatch(id);
|
|
71
90
|
this.traceStack.push(b);
|
|
72
|
-
b.start(
|
|
91
|
+
b.start();
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.error(`${ ' '.repeat((this.traceStack.length - 1) * 2) }${ id } started`);
|
|
73
94
|
}
|
|
74
95
|
catch (e) {
|
|
75
96
|
// eslint-disable-next-line no-console
|
|
@@ -86,7 +107,10 @@ class TimeTracer {
|
|
|
86
107
|
stop() {
|
|
87
108
|
try {
|
|
88
109
|
const current = this.traceStack.pop();
|
|
89
|
-
current.stop(
|
|
110
|
+
const dT = current.stop();
|
|
111
|
+
const base = `${ ' '.repeat(this.traceStack.length * 2) }${ current.id } took:`;
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, dT[0], dT[1] / 1000000);
|
|
90
114
|
}
|
|
91
115
|
catch (e) {
|
|
92
116
|
// eslint-disable-next-line no-console
|
|
@@ -101,4 +125,8 @@ const ignoreTimeTrace = {
|
|
|
101
125
|
};
|
|
102
126
|
|
|
103
127
|
const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
|
|
104
|
-
module.exports =
|
|
128
|
+
module.exports = {
|
|
129
|
+
timetrace: (doTimeTrace ? new TimeTracer() : ignoreTimeTrace),
|
|
130
|
+
TimeTracer,
|
|
131
|
+
StopWatch,
|
|
132
|
+
};
|
package/package.json
CHANGED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* If a mixin association is published, return the mixin association.
|
|
5
|
-
*
|
|
6
|
-
* @param {CSN.Query} query Query of the artifact to check
|
|
7
|
-
* @param {object} association Association (Element) published by the view
|
|
8
|
-
* @param {string} associationName
|
|
9
|
-
* @returns {object} The mixin association
|
|
10
|
-
*/
|
|
11
|
-
function getMixinAssocOfQueryIfPublished(query, association, associationName) {
|
|
12
|
-
if (query && query.SELECT && query.SELECT.mixin) {
|
|
13
|
-
const aliasedColumnsMap = Object.create(null);
|
|
14
|
-
if (query.SELECT.columns) {
|
|
15
|
-
for (const column of query.SELECT.columns) {
|
|
16
|
-
if (column.as && column.ref && column.ref.length === 1)
|
|
17
|
-
aliasedColumnsMap[column.as] = column;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
for (const elem of Object.keys(query.SELECT.mixin)) {
|
|
22
|
-
const mixinElement = query.SELECT.mixin[elem];
|
|
23
|
-
let originalName = associationName;
|
|
24
|
-
if (aliasedColumnsMap[associationName])
|
|
25
|
-
originalName = aliasedColumnsMap[associationName].ref[0];
|
|
26
|
-
|
|
27
|
-
if (elem === originalName)
|
|
28
|
-
return { mixinElement, mixinName: originalName };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Check wether the given artifact uses the given mixin association.
|
|
36
|
-
*
|
|
37
|
-
* @param {CSN.Query} query Query of the artifact to check
|
|
38
|
-
* @param {object} association Mixin association (Element) to check for
|
|
39
|
-
* @param {string} associationName
|
|
40
|
-
* @returns {boolean} True if used
|
|
41
|
-
*/
|
|
42
|
-
function usesMixinAssociation(query, association, associationName) {
|
|
43
|
-
if (query && query.SELECT && query.SELECT.columns) {
|
|
44
|
-
for (const column of query.SELECT.columns) {
|
|
45
|
-
if (typeof column === 'object' && column.ref && column.ref.length > 1 && (column.ref[0] === associationName || column.ref[0].id === associationName)) {
|
|
46
|
-
// FIXME: This is not necessarily correct: the assoc name needs not be the first component, as e.g. $projection.assoc
|
|
47
|
-
// would be also valid. Check other paths like $self.assoc ....
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = {
|
|
56
|
-
usesMixinAssociation,
|
|
57
|
-
getMixinAssocOfQueryIfPublished,
|
|
58
|
-
};
|