@sap/cds-compiler 2.5.2 → 2.11.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 +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
|
|
4
|
-
var {
|
|
5
|
-
|
|
4
|
+
var { makeMessageFunction } = require('../base/messages');
|
|
5
|
+
const { recompileX } = require('../compiler/index');
|
|
6
|
+
var { linkToOrigin } = require('../compiler/utils');
|
|
6
7
|
const {compactModel, compactExpr} = require('../json/to-csn');
|
|
7
8
|
const { deduplicateMessages } = require('../base/messages');
|
|
8
|
-
const timetrace = require('../utils/timetrace');
|
|
9
|
+
const { timetrace } = require('../utils/timetrace');
|
|
9
10
|
// Paths that start with an artifact of protected kind are special
|
|
10
11
|
// either ignore them in QAT building or in path rewriting
|
|
11
12
|
const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
|
|
12
13
|
|
|
13
14
|
function translateAssocsToJoinsCSN(csn, options){
|
|
14
15
|
timetrace.start('Recompiling model');
|
|
15
|
-
let { augment } = require('../json/from-csn');
|
|
16
|
-
// Append `.csn` to `.cds` files to indicate recompilation.
|
|
17
|
-
const file = csn.$location && csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
|
|
18
|
-
let xsn = augment(csn, file, options);
|
|
19
|
-
const { compileSourcesX } = require('../compiler');
|
|
20
16
|
// Do not re-complain about localized
|
|
21
|
-
const
|
|
17
|
+
const compileOptions = { ...options, $skipNameCheck: true };
|
|
18
|
+
delete compileOptions.csnFlavor;
|
|
19
|
+
const model = recompileX(csn, compileOptions);
|
|
22
20
|
timetrace.stop();
|
|
23
21
|
timetrace.start('Translating associations to joins');
|
|
24
22
|
translateAssocsToJoins(model, options);
|
|
@@ -46,9 +44,9 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
49
|
-
|
|
47
|
+
makeMessageFunction(model, options).throwWithError();
|
|
50
48
|
// FIXME: Move this somewhere more appropriate
|
|
51
|
-
const compact = compactModel(model,
|
|
49
|
+
const compact = compactModel(model, compileOptions);
|
|
52
50
|
return compact;
|
|
53
51
|
}
|
|
54
52
|
|
|
@@ -181,7 +179,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
181
179
|
let joinTree = query.from;
|
|
182
180
|
for(let tan in query.$tableAliases)
|
|
183
181
|
{
|
|
184
|
-
if(
|
|
182
|
+
if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
|
|
185
183
|
{
|
|
186
184
|
let ta = query.$tableAliases[tan];
|
|
187
185
|
joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
|
|
@@ -202,7 +200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
202
200
|
function createQAForFromClauseSubQuery(query, env)
|
|
203
201
|
{
|
|
204
202
|
for (let taName in query.$tableAliases) {
|
|
205
|
-
if (
|
|
203
|
+
if (query.$tableAliases[taName].kind !== '$self') {
|
|
206
204
|
let ta = query.$tableAliases[taName];
|
|
207
205
|
if(!ta.$QA) {
|
|
208
206
|
ta.$QA = createQA(env, ta._origin, taName, undefined);
|
|
@@ -243,14 +241,6 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
243
241
|
{
|
|
244
242
|
art.$QA = createQA(env, art.target._artifact, art.name.id );
|
|
245
243
|
art.$QA.mixin = true;
|
|
246
|
-
/* Mark mixin definition to be _ignored:
|
|
247
|
-
- If the mixin is used, it is now resolved into a join => definition vaporizes
|
|
248
|
-
- If the mixin is published, forHana backend must create a __copy with rewritten
|
|
249
|
-
$projection ON conditon and publish it with alias.
|
|
250
|
-
- If the mixin is neither be used nor published it shall not be visible to the database
|
|
251
|
-
(internal mixin).
|
|
252
|
-
*/
|
|
253
|
-
art.$a2j = { _ignore: true };
|
|
254
244
|
}
|
|
255
245
|
});
|
|
256
246
|
}
|
|
@@ -618,7 +608,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
618
608
|
const id = this.id();
|
|
619
609
|
if(elt) {
|
|
620
610
|
let found = true;
|
|
621
|
-
const epath = elt
|
|
611
|
+
const epath = [elt];
|
|
622
612
|
const epl = epath.length+offset;
|
|
623
613
|
if(epl < path.length) {
|
|
624
614
|
for(let i = 0; i < epl && found; i++) {
|
|
@@ -630,7 +620,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
630
620
|
}
|
|
631
621
|
if(id) {
|
|
632
622
|
let found = true;
|
|
633
|
-
const epath = id
|
|
623
|
+
const epath = [id];
|
|
634
624
|
const epl = epath.length+offset;
|
|
635
625
|
if(epl < path.length) {
|
|
636
626
|
for(let i = 0; i < epl && found; i++) {
|
|
@@ -745,9 +735,14 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
745
735
|
function swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias) {
|
|
746
736
|
let newSrcAlias = tgtAlias;
|
|
747
737
|
let newTgtAlias = {};
|
|
748
|
-
// first try to identify table alias for complex views or
|
|
749
|
-
|
|
750
|
-
|
|
738
|
+
// first try to identify table alias for complex views or redirected associations
|
|
739
|
+
if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
|
|
740
|
+
// redirected target must have a $QA
|
|
741
|
+
fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA &&
|
|
742
|
+
// $QA's artifact must either be same srcAlias artifact
|
|
743
|
+
(fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA._artifact === srcAlias._artifact ||
|
|
744
|
+
// OR original assoc is a mixin (then just use the $QA)
|
|
745
|
+
assoc.kind === 'mixin')) {
|
|
751
746
|
newTgtAlias.id = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.name.id;
|
|
752
747
|
newTgtAlias._artifact = fwdAssoc._redirected[fwdAssoc._redirected.length-1]._effectiveType;
|
|
753
748
|
newTgtAlias._navigation = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.path[0]._navigation;
|
|
@@ -1400,7 +1395,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1400
1395
|
|
|
1401
1396
|
let [head, ...tail] = path;
|
|
1402
1397
|
|
|
1403
|
-
if(['$projection', '$self'].includes(head.id) && tail.length) {
|
|
1398
|
+
if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
|
|
1404
1399
|
// make sure not to truncate tail
|
|
1405
1400
|
if(tail.length > 1)
|
|
1406
1401
|
[head, ...tail] = tail;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { forEachDefinition } = require('../base/model');
|
|
4
|
+
const {
|
|
5
|
+
applyTransformations,
|
|
6
|
+
cloneCsn,
|
|
7
|
+
getUtils,
|
|
8
|
+
isBuiltinType,
|
|
9
|
+
} = require('../model/csnUtils');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Loop through a universal CSN and enrich it with the properties
|
|
13
|
+
* from the source definition - modifies the input model in-place
|
|
14
|
+
*
|
|
15
|
+
* @param {CSN.Model} csn
|
|
16
|
+
* @param {CSN.Options} options
|
|
17
|
+
*/
|
|
18
|
+
module.exports = function(csn, options) {
|
|
19
|
+
let { getOrigin, getFinalType, getFinalTypeDef } = getUtils(csn);
|
|
20
|
+
// User-defined structured types do not have the elements propagated any longer
|
|
21
|
+
// if there is no association among the elements. For that reason,
|
|
22
|
+
// as a first step propagate the elements of these
|
|
23
|
+
forEachDefinition(csn, (def) => {
|
|
24
|
+
if (def.kind === 'type' && def.type && !def.elements) {
|
|
25
|
+
const finalType = getFinalType(def.type);
|
|
26
|
+
if (isBuiltinType(finalType)) return;
|
|
27
|
+
const finalTypeDef = getFinalTypeDef(def.type);
|
|
28
|
+
if (finalTypeDef.elements)
|
|
29
|
+
def.elements = cloneCsn(finalTypeDef.elements, options);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// as a second step, loop through all the $origin properties in the model
|
|
34
|
+
// and propagate the properties from the origin definition
|
|
35
|
+
applyTransformations(csn, {
|
|
36
|
+
'$origin': (node, _$orign, $originValue, _path, parent, propName) => {
|
|
37
|
+
if (!node.kind) { // we do not want to replace whole definitions
|
|
38
|
+
if (Array.isArray($originValue))
|
|
39
|
+
propagatePropsFromOrigin(node, propName, parent);
|
|
40
|
+
else if ($originValue.$origin && Array.isArray($originValue.$origin)) {
|
|
41
|
+
// cover the case of query entity elements where we have own and ihnerited attributes/annotations
|
|
42
|
+
propagatePropsFromOrigin($originValue, propName, parent);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}, undefined, undefined, options);
|
|
48
|
+
|
|
49
|
+
function propagatePropsFromOrigin(member, memberName, construct) {
|
|
50
|
+
// TODO: shall the $origin be kept as part of the element?
|
|
51
|
+
const origin = getOrigin(member);
|
|
52
|
+
if (origin.kind) return;
|
|
53
|
+
if (member.elements && origin.type) {
|
|
54
|
+
delete member.$origin;
|
|
55
|
+
member.type = origin.type;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let newMember = cloneCsn(origin, options);
|
|
59
|
+
// keep targets and keys of assoc, if it was redirected
|
|
60
|
+
if (origin.type === 'cds.Association') {
|
|
61
|
+
newMember.target = member.target || newMember.target;
|
|
62
|
+
newMember.keys = member.keys || newMember.keys;
|
|
63
|
+
}
|
|
64
|
+
// TODO: check if this works fine for items/returns/actions
|
|
65
|
+
construct[memberName] = newMember;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/lib/utils/file.js
CHANGED
|
@@ -15,16 +15,6 @@ function splitLines(src) {
|
|
|
15
15
|
return src.split(/\r\n?|\n/);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Change Windows style line endings to Unix style
|
|
20
|
-
*
|
|
21
|
-
* @param {string} src
|
|
22
|
-
* @returns {string}
|
|
23
|
-
*/
|
|
24
|
-
function normalizeLineEndings(src) {
|
|
25
|
-
return (src && process.platform === 'win32') ? src.replace(/\r\n/g, '\n') : src;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
18
|
/**
|
|
29
19
|
* Returns filesystem utils readFile(), isFile(), realpath() for _CDS_ usage.
|
|
30
20
|
* This includes a trace as well as usage of a file cache.
|
|
@@ -40,7 +30,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
40
30
|
const readFile = _wrapReadFileCached(fs.readFile);
|
|
41
31
|
const readFileSync = _wrapReadFileCached((filename, enc, cb) => {
|
|
42
32
|
try {
|
|
43
|
-
cb(null, fs.readFileSync( filename, enc ));
|
|
33
|
+
cb(null, fs.readFileSync( filename, { encoding: enc } ));
|
|
44
34
|
}
|
|
45
35
|
catch (err) {
|
|
46
36
|
cb(err, null);
|
|
@@ -82,7 +72,7 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
82
72
|
* Wraps the given reader into a cached environment including a trace.
|
|
83
73
|
* The given @p reader must have the same signature as fs.readFile.
|
|
84
74
|
*
|
|
85
|
-
* @param {(filename: string, enc
|
|
75
|
+
* @param {(filename: string, enc, cb: (err, data) => void) => void} reader
|
|
86
76
|
*/
|
|
87
77
|
function _wrapReadFileCached( reader ) {
|
|
88
78
|
return (filename, enc, cb) => {
|
|
@@ -103,7 +93,9 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
103
93
|
body.syscall = 'open';
|
|
104
94
|
body.path = filename;
|
|
105
95
|
}
|
|
106
|
-
if (body
|
|
96
|
+
if (body && body.stack && body.message) {
|
|
97
|
+
// NOTE: checks for instanceof Error are not reliable if error
|
|
98
|
+
// created in different execution env
|
|
107
99
|
traceFS( 'READFILE:cache-error:', filename, body.message );
|
|
108
100
|
cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
|
|
109
101
|
}
|
|
@@ -181,6 +173,5 @@ function cdsFs(fileCache, enableTrace) {
|
|
|
181
173
|
|
|
182
174
|
module.exports = {
|
|
183
175
|
splitLines,
|
|
184
|
-
normalizeLineEndings,
|
|
185
176
|
cdsFs,
|
|
186
177
|
};
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
|
|
10
|
-
const { makeMessageFunction } = require('../base/messages');
|
|
11
10
|
const { cdsFs } = require('./file');
|
|
12
11
|
|
|
13
12
|
const DEFAULT_ENCODING = 'utf-8';
|
|
@@ -44,7 +43,7 @@ function adaptCdsModule(modulePath) {
|
|
|
44
43
|
* @param {object} fileCache
|
|
45
44
|
* @param {CSN.Options} options
|
|
46
45
|
*/
|
|
47
|
-
function resolveModule( dep, fileCache, options ) {
|
|
46
|
+
function resolveModule( dep, fileCache, options, messageFunctions ) {
|
|
48
47
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
49
48
|
// let opts = { extensions, basedir: dep.basedir, preserveSymlinks: false };
|
|
50
49
|
// `preserveSymlinks` option does not really work -> provide workaround anyway...
|
|
@@ -96,7 +95,7 @@ function resolveModule( dep, fileCache, options ) {
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
}).catch( () => {
|
|
99
|
-
_errorFileNotFound(dep, options);
|
|
98
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
100
99
|
return false;
|
|
101
100
|
});
|
|
102
101
|
}
|
|
@@ -107,7 +106,7 @@ function resolveModule( dep, fileCache, options ) {
|
|
|
107
106
|
* @param {object} fileCache
|
|
108
107
|
* @param {CSN.Options} options
|
|
109
108
|
*/
|
|
110
|
-
function resolveModuleSync( dep, fileCache, options ) {
|
|
109
|
+
function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
|
|
111
110
|
const _fs = cdsFs(fileCache, options.traceFs);
|
|
112
111
|
const opts = {
|
|
113
112
|
extensions,
|
|
@@ -129,7 +128,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
129
128
|
});
|
|
130
129
|
|
|
131
130
|
if (error) {
|
|
132
|
-
_errorFileNotFound(dep, options);
|
|
131
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
133
132
|
return false;
|
|
134
133
|
}
|
|
135
134
|
|
|
@@ -148,7 +147,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
if (error) {
|
|
151
|
-
_errorFileNotFound(dep, options);
|
|
150
|
+
_errorFileNotFound(dep, options, messageFunctions);
|
|
152
151
|
return false;
|
|
153
152
|
}
|
|
154
153
|
|
|
@@ -161,8 +160,7 @@ function resolveModuleSync( dep, fileCache, options ) {
|
|
|
161
160
|
return result;
|
|
162
161
|
}
|
|
163
162
|
|
|
164
|
-
function _errorFileNotFound(dep, options) {
|
|
165
|
-
const { error } = makeMessageFunction( null, options, 'compile' );
|
|
163
|
+
function _errorFileNotFound(dep, options, { error }) {
|
|
166
164
|
if (dep.resolved) {
|
|
167
165
|
let resolved = path.relative( dep.basedir, dep.resolved );
|
|
168
166
|
if (options.testMode)
|
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
|
@@ -5,7 +5,7 @@
|
|
|
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,46 @@ class TimeTrace {
|
|
|
13
13
|
* @memberOf TimeTrace
|
|
14
14
|
*/
|
|
15
15
|
constructor(id) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* @param {number} indent
|
|
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
|
+
// eslint-disable-next-line no-multi-assign
|
|
18
|
+
this.startTime = this.lapTime = process.hrtime();
|
|
19
|
+
}
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* @param {number} indent
|
|
21
|
+
/**
|
|
22
|
+
* Start watch.
|
|
32
23
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
start() {
|
|
25
|
+
// eslint-disable-next-line no-multi-assign
|
|
26
|
+
this.startTime = this.lapTime = process.hrtime();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Stop and return delta T
|
|
31
|
+
* but do not set start time
|
|
32
|
+
*/
|
|
33
|
+
stop() {
|
|
34
|
+
return process.hrtime(this.startTime);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* return lap time
|
|
39
|
+
*/
|
|
40
|
+
lap() {
|
|
41
|
+
const dt = process.hrtime(this.lapTime);
|
|
42
|
+
this.lapTime = process.hrtime();
|
|
43
|
+
return dt;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// stop as sec.ns float
|
|
47
|
+
stopInFloatSecs() {
|
|
48
|
+
const dt = this.stop();
|
|
49
|
+
return dt[0] + dt[1] / 1000000000;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// lap as sec.ns float
|
|
53
|
+
lapInFloatSecs() {
|
|
54
|
+
const dt = this.lap();
|
|
55
|
+
return dt[0] + dt[1] / 1000000000;
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
58
|
|
|
@@ -67,9 +84,11 @@ class TimeTracer {
|
|
|
67
84
|
*/
|
|
68
85
|
start(id) {
|
|
69
86
|
try {
|
|
70
|
-
const b = new
|
|
87
|
+
const b = new StopWatch(id);
|
|
71
88
|
this.traceStack.push(b);
|
|
72
|
-
b.start(
|
|
89
|
+
b.start();
|
|
90
|
+
// eslint-disable-next-line no-console
|
|
91
|
+
console.error(`${ ' '.repeat((this.traceStack.length - 1) * 2) }${ id } started`);
|
|
73
92
|
}
|
|
74
93
|
catch (e) {
|
|
75
94
|
// eslint-disable-next-line no-console
|
|
@@ -86,7 +105,10 @@ class TimeTracer {
|
|
|
86
105
|
stop() {
|
|
87
106
|
try {
|
|
88
107
|
const current = this.traceStack.pop();
|
|
89
|
-
current.stop(
|
|
108
|
+
const dT = current.stop();
|
|
109
|
+
const base = `${ ' '.repeat(this.traceStack.length * 2) }${ current.id } took:`;
|
|
110
|
+
// eslint-disable-next-line no-console
|
|
111
|
+
console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, dT[0], dT[1] / 1000000);
|
|
90
112
|
}
|
|
91
113
|
catch (e) {
|
|
92
114
|
// eslint-disable-next-line no-console
|
|
@@ -101,4 +123,4 @@ const ignoreTimeTrace = {
|
|
|
101
123
|
};
|
|
102
124
|
|
|
103
125
|
const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
|
|
104
|
-
module.exports = doTimeTrace ? new TimeTracer() : ignoreTimeTrace;
|
|
126
|
+
module.exports = { timetrace: (doTimeTrace ? new TimeTracer() : ignoreTimeTrace), StopWatch };
|
package/package.json
CHANGED
package/lib/json/walker.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Walker module provides non-recursive tree walkers with different flavours
|
|
3
|
-
* @module json/walker
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Callback of the forEach function called for each node it walks
|
|
8
|
-
* @callback forEachCallback
|
|
9
|
-
* @param {string} name of the node
|
|
10
|
-
* @param {object} node
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Loops over all elements in an object and calls the specified callback(key,obj)
|
|
15
|
-
* @param {object} obj
|
|
16
|
-
* @param {forEachCallback} callback
|
|
17
|
-
*/
|
|
18
|
-
function forEach(obj, callback) {
|
|
19
|
-
for(var key in obj) {
|
|
20
|
-
callback(key, obj[key]);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
forEach,
|
|
26
|
-
}
|
package/lib/transform/sqlite
DELETED
|
File without changes
|
package/lib/utils/string.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// String utils
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Removes duplicate entries from the given string array.
|
|
7
|
-
*
|
|
8
|
-
* @param {string[]} strings Array of strings with duplicates
|
|
9
|
-
* @returns {string[]} strings New array with unique strings
|
|
10
|
-
*/
|
|
11
|
-
function uniqueStrings(strings) {
|
|
12
|
-
return [ ...new Set(strings) ];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
module.exports = {
|
|
16
|
-
uniqueStrings,
|
|
17
|
-
};
|