@sap/cds-compiler 2.7.0 → 2.11.2

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.
Files changed (87) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +10 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +17 -33
  7. package/lib/api/options.js +25 -13
  8. package/lib/api/validate.js +33 -9
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +26 -2
  13. package/lib/base/messages.js +25 -9
  14. package/lib/base/model.js +5 -3
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/onConditions.js +5 -0
  17. package/lib/checks/selectItems.js +4 -0
  18. package/lib/checks/types.js +26 -2
  19. package/lib/checks/unknownMagic.js +41 -0
  20. package/lib/checks/validator.js +7 -2
  21. package/lib/compiler/assert-consistency.js +18 -5
  22. package/lib/compiler/base.js +65 -0
  23. package/lib/compiler/builtins.js +30 -1
  24. package/lib/compiler/checks.js +5 -2
  25. package/lib/compiler/definer.js +145 -120
  26. package/lib/compiler/index.js +16 -4
  27. package/lib/compiler/propagator.js +5 -2
  28. package/lib/compiler/resolver.js +207 -47
  29. package/lib/compiler/shared.js +47 -200
  30. package/lib/compiler/utils.js +173 -0
  31. package/lib/edm/annotations/genericTranslation.js +183 -187
  32. package/lib/edm/csn2edm.js +94 -98
  33. package/lib/edm/edm.js +16 -20
  34. package/lib/edm/edmPreprocessor.js +302 -115
  35. package/lib/edm/edmUtils.js +31 -12
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +28 -1
  38. package/lib/gen/language.tokens +79 -69
  39. package/lib/gen/languageLexer.interp +28 -1
  40. package/lib/gen/languageLexer.js +879 -805
  41. package/lib/gen/languageLexer.tokens +71 -62
  42. package/lib/gen/languageParser.js +5308 -4308
  43. package/lib/json/from-csn.js +59 -30
  44. package/lib/json/to-csn.js +354 -105
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +81 -14
  48. package/lib/language/language.g4 +163 -31
  49. package/lib/main.d.ts +136 -17
  50. package/lib/main.js +7 -1
  51. package/lib/model/api.js +78 -0
  52. package/lib/model/csnRefs.js +115 -32
  53. package/lib/model/csnUtils.js +71 -33
  54. package/lib/model/enrichCsn.js +36 -9
  55. package/lib/model/revealInternalProperties.js +20 -4
  56. package/lib/modelCompare/compare.js +2 -1
  57. package/lib/optionProcessor.js +33 -16
  58. package/lib/render/.eslintrc.json +3 -1
  59. package/lib/render/DuplicateChecker.js +1 -1
  60. package/lib/render/toCdl.js +60 -17
  61. package/lib/render/toHdbcds.js +122 -74
  62. package/lib/render/toSql.js +57 -32
  63. package/lib/render/utils/common.js +6 -10
  64. package/lib/sql-identifier.js +6 -1
  65. package/lib/transform/db/constraints.js +273 -119
  66. package/lib/transform/db/draft.js +9 -6
  67. package/lib/transform/db/expansion.js +19 -7
  68. package/lib/transform/db/flattening.js +31 -7
  69. package/lib/transform/db/transformExists.js +344 -66
  70. package/lib/transform/db/views.js +438 -0
  71. package/lib/transform/forHanaNew.js +65 -436
  72. package/lib/transform/forOdataNew.js +21 -10
  73. package/lib/transform/localized.js +2 -0
  74. package/lib/transform/odata/attachPath.js +19 -4
  75. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  76. package/lib/transform/odata/referenceFlattener.js +44 -38
  77. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  78. package/lib/transform/odata/structuralPath.js +72 -0
  79. package/lib/transform/odata/structureFlattener.js +13 -10
  80. package/lib/transform/odata/typesExposure.js +22 -12
  81. package/lib/transform/transformUtilsNew.js +55 -9
  82. package/lib/transform/translateAssocsToJoins.js +11 -17
  83. package/lib/transform/universalCsnEnricher.js +67 -0
  84. package/lib/utils/file.js +5 -3
  85. package/lib/utils/term.js +65 -42
  86. package/lib/utils/timetrace.js +48 -26
  87. package/package.json +1 -1
@@ -61,6 +61,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
61
61
  recurseElements,
62
62
  renameAnnotation,
63
63
  setAnnotation,
64
+ resetAnnotation,
64
65
  expandStructsInExpression,
65
66
  };
66
67
 
@@ -79,6 +80,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
79
80
  else if(defStrLen5k)
80
81
  element.length = 5000;
81
82
  }
83
+ if (element.type === 'cds.Binary' && element.length === undefined) {
84
+ if(options.defaultBinaryLength) {
85
+ element.length = options.defaultBinaryLength;
86
+ setProp(element, '$default', true);
87
+ }
88
+ else if(defStrLen5k)
89
+ element.length = 5000;
90
+ }
82
91
  /*
83
92
  if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
84
93
  element.precision = options.precision;
@@ -342,6 +351,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
342
351
 
343
352
  /**
344
353
  * Copy properties of the referenced type, but don't resolve to the final base type.
354
+ *
355
+ * Do not copy the length if it was just set via the default-value.
345
356
  *
346
357
  * @param {any} node Node to copy to
347
358
  * @returns {void}
@@ -378,9 +389,10 @@ function getTransformers(model, options, pathDelimiter = '_') {
378
389
  *
379
390
  * @param {CSN.Artifact} node
380
391
  * @param {WeakMap} [resolved] WeakMap containing already resolved refs
392
+ * @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
381
393
  * @returns {void}
382
394
  */
383
- function toFinalBaseType(node, resolved) {
395
+ function toFinalBaseType(node, resolved, keepLocalized=false) {
384
396
  // Nothing to do if no type (or if array/struct type)
385
397
  if (!node || !node.type) return;
386
398
  // In case of a ref -> Follow the ref
@@ -440,6 +452,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
440
452
  Object.assign(node, { scale: typeDef.scale });
441
453
  if (node.srid === undefined && typeDef.srid !== undefined)
442
454
  Object.assign(node, { srid: typeDef.srid });
455
+ if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
456
+ Object.assign(node, { localized: typeDef.localized });
443
457
  node.type = typeDef.type;
444
458
  toFinalBaseType(node);
445
459
  }
@@ -920,6 +934,34 @@ function getTransformers(model, options, pathDelimiter = '_') {
920
934
  node[name] = value;
921
935
  }
922
936
 
937
+ /**
938
+ * Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
939
+ * Overwritting is when the assignment differs from undefined and null, also when differs from the already set value.
940
+ * Setting new assignment results false as return value and overwriting - true.
941
+ *
942
+ * @param {object} node Assignee
943
+ * @param {string} name Annotation name
944
+ * @param {any} value Annotation value
945
+ * @param {function} info function that reports info messages
946
+ * @param {CSN.Path} path location of the warning
947
+ * @returns {boolean} wasOverwritten true when the annotation was overwritten
948
+ */
949
+ function resetAnnotation(node, name, value, info, path) {
950
+ if (!name.startsWith('@')) {
951
+ throw Error('Annotation name should start with "@": ' + name);
952
+ }
953
+ if (value === undefined) {
954
+ throw Error('Annotation value must not be undefined');
955
+ }
956
+
957
+ const wasOverwritten = node[name] !== undefined && node[name] !== null && node[name] !== value;
958
+ const oldValue = node[name];
959
+ node[name] = value;
960
+ if(wasOverwritten)
961
+ info(null, path, { anno: name, prop: value, otherprop: oldValue },
962
+ `Value $(OTHERPROP) of annotation $(ANNO) is overwritten with new value $(PROP)`);
963
+ return wasOverwritten;
964
+ }
923
965
 
924
966
  /*
925
967
  Resolve the type of an artifact
@@ -1052,16 +1094,16 @@ function getTransformers(model, options, pathDelimiter = '_') {
1052
1094
  function expandStructsInExpression(csn, options = {}) {
1053
1095
  applyTransformations(csn, {
1054
1096
  'on': (parent, name, on, path) => {
1055
- parent.on = expand(parent.on, path);
1097
+ parent.on = expand(parent.on, path.concat(name));
1056
1098
  },
1057
1099
  'having': (parent, name, having, path) => {
1058
- parent.having = expand(parent.having, path);
1100
+ parent.having = expand(parent.having, path.concat(name));
1059
1101
  },
1060
1102
  'where': (parent, name, where, path) => {
1061
- parent.where = expand(parent.where, path);
1103
+ parent.where = expand(parent.where, path.concat(name));
1062
1104
  },
1063
1105
  'xpr': (parent, name, xpr, path) => {
1064
- parent.xpr = expand(parent.xpr, path);
1106
+ parent.xpr = expand(parent.xpr, path.concat(name));
1065
1107
  }
1066
1108
  }, undefined, undefined, options);
1067
1109
 
@@ -1079,17 +1121,21 @@ function getTransformers(model, options, pathDelimiter = '_') {
1079
1121
  if(i < expr.length-2)
1080
1122
  {
1081
1123
  const [lhs, op, rhs] = expr.slice(i);
1124
+
1125
+ // we might have to ad-hoc resolve a ref, since handleExists is run before hand and generates new refs.
1126
+ const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
1127
+ const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
1082
1128
  // lhs & rhs must be expandable types (structures or managed associations)
1083
- if(lhs._art && rhs._art &&
1129
+ if(lhsArt && rhsArt &&
1084
1130
  lhs.ref && rhs.ref &&
1085
- isExpandable(lhs._art) && isExpandable(rhs._art) &&
1131
+ isExpandable(lhsArt) && isExpandable(rhsArt) &&
1086
1132
  ['=', '<', '>', '>=', '<=', '!=', '<>'].includes(op) &&
1087
1133
  !(isDollarSelfOrProjectionOperand(lhs) || isDollarSelfOrProjectionOperand(rhs))) {
1088
1134
 
1089
1135
  // if path is scalar and no assoc or has no type (@Core.Computed) use original expression
1090
1136
  // only do the expansion on (managed) assocs and (items.)elements, array of check in ON cond is done elsewhere
1091
- const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art: lhs._art, ref: lhs.ref }, false, true );
1092
- const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art: rhs._art, ref: rhs.ref }, false, true );
1137
+ const lhspaths = /*isScalarOrNoType(lhs._art) ? [ lhs ] : */ flattenPath({ _art: lhsArt, ref: lhs.ref }, false, true );
1138
+ const rhspaths = /*isScalarOrNoType(rhs._art) ? [ rhs ] : */ flattenPath({ _art: rhsArt, ref: rhs.ref }, false, true );
1093
1139
 
1094
1140
  // mapping dict for lhs/rhs for mismatch check
1095
1141
  // strip lhs/rhs prefix from flattened paths to check remaining common trailing path
@@ -1,12 +1,12 @@
1
1
  'use strict'
2
2
 
3
3
  const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
4
- var { handleMessages, makeMessageFunction } = require('../base/messages');
4
+ var { makeMessageFunction } = require('../base/messages');
5
5
  const { recompileX } = require('../compiler/index');
6
- var { linkToOrigin } = require('../compiler/shared');
6
+ var { linkToOrigin } = require('../compiler/utils');
7
7
  const {compactModel, compactExpr} = require('../json/to-csn');
8
8
  const { deduplicateMessages } = require('../base/messages');
9
- const timetrace = require('../utils/timetrace');
9
+ const { timetrace } = require('../utils/timetrace');
10
10
  // Paths that start with an artifact of protected kind are special
11
11
  // either ignore them in QAT building or in path rewriting
12
12
  const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
@@ -14,7 +14,9 @@ const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
14
14
  function translateAssocsToJoinsCSN(csn, options){
15
15
  timetrace.start('Recompiling model');
16
16
  // Do not re-complain about localized
17
- const model = recompileX(csn, { ...options, $skipNameCheck: true });
17
+ const compileOptions = { ...options, $skipNameCheck: true };
18
+ delete compileOptions.csnFlavor;
19
+ const model = recompileX(csn, compileOptions);
18
20
  timetrace.stop();
19
21
  timetrace.start('Translating associations to joins');
20
22
  translateAssocsToJoins(model, options);
@@ -42,9 +44,9 @@ function translateAssocsToJoinsCSN(csn, options){
42
44
  }
43
45
 
44
46
  // If A2J reports error - end! Continuing with a broken CSN makes no sense
45
- handleMessages(model, options);
47
+ makeMessageFunction(model, options).throwWithError();
46
48
  // FIXME: Move this somewhere more appropriate
47
- const compact = compactModel(model, options);
49
+ const compact = compactModel(model, compileOptions);
48
50
  return compact;
49
51
  }
50
52
 
@@ -177,7 +179,7 @@ function translateAssocsToJoins(model, inputOptions = {})
177
179
  let joinTree = query.from;
178
180
  for(let tan in query.$tableAliases)
179
181
  {
180
- if(!['$projection', '$self'].includes(tan)) // don't drive into $projection/$self tableAlias (yet)
182
+ if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
181
183
  {
182
184
  let ta = query.$tableAliases[tan];
183
185
  joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
@@ -198,7 +200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
198
200
  function createQAForFromClauseSubQuery(query, env)
199
201
  {
200
202
  for (let taName in query.$tableAliases) {
201
- if (!['$self', '$projection'].includes(taName)) {
203
+ if (query.$tableAliases[taName].kind !== '$self') {
202
204
  let ta = query.$tableAliases[taName];
203
205
  if(!ta.$QA) {
204
206
  ta.$QA = createQA(env, ta._origin, taName, undefined);
@@ -239,14 +241,6 @@ function translateAssocsToJoins(model, inputOptions = {})
239
241
  {
240
242
  art.$QA = createQA(env, art.target._artifact, art.name.id );
241
243
  art.$QA.mixin = true;
242
- /* Mark mixin definition to be _ignored:
243
- - If the mixin is used, it is now resolved into a join => definition vaporizes
244
- - If the mixin is published, forHana backend must create a __copy with rewritten
245
- $projection ON conditon and publish it with alias.
246
- - If the mixin is neither be used nor published it shall not be visible to the database
247
- (internal mixin).
248
- */
249
- art.$a2j = { _ignore: true };
250
244
  }
251
245
  });
252
246
  }
@@ -1401,7 +1395,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1401
1395
 
1402
1396
  let [head, ...tail] = path;
1403
1397
 
1404
- if(['$projection', '$self'].includes(head.id) && tail.length) {
1398
+ if(['$projection', '$self'].includes(head.id) && tail.length && head._navigation.kind === '$self') {
1405
1399
  // make sure not to truncate tail
1406
1400
  if(tail.length > 1)
1407
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
@@ -30,7 +30,7 @@ function cdsFs(fileCache, enableTrace) {
30
30
  const readFile = _wrapReadFileCached(fs.readFile);
31
31
  const readFileSync = _wrapReadFileCached((filename, enc, cb) => {
32
32
  try {
33
- cb(null, fs.readFileSync( filename, enc ));
33
+ cb(null, fs.readFileSync( filename, { encoding: enc } ));
34
34
  }
35
35
  catch (err) {
36
36
  cb(err, null);
@@ -72,7 +72,7 @@ function cdsFs(fileCache, enableTrace) {
72
72
  * Wraps the given reader into a cached environment including a trace.
73
73
  * The given @p reader must have the same signature as fs.readFile.
74
74
  *
75
- * @param {(filename: string, enc: string, cb: (err, data) => void) => void} reader
75
+ * @param {(filename: string, enc, cb: (err, data) => void) => void} reader
76
76
  */
77
77
  function _wrapReadFileCached( reader ) {
78
78
  return (filename, enc, cb) => {
@@ -93,7 +93,9 @@ function cdsFs(fileCache, enableTrace) {
93
93
  body.syscall = 'open';
94
94
  body.path = filename;
95
95
  }
96
- if (body instanceof Error) {
96
+ if (body && body.stack && body.message) {
97
+ // NOTE: checks for instanceof Error are not reliable if error
98
+ // created in different execution env
97
99
  traceFS( 'READFILE:cache-error:', filename, body.message );
98
100
  cb( body ); // no need for process.nextTick( cb, body ) with moduleResolve
99
101
  }
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. stderr/stdout are no TTYs if they (for example)
6
- // are piped to another process or written to file:
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
- let hasColor = stdoutHasColor && stderrHasColor;
21
-
22
- module.exports.useColor = (mode) => {
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
- const as = (codes, o) => (hasColor ? (codes + o + t.reset) : (`${ o }`));
36
+ function term(useColor = 'auto') {
37
+ let hasColor = hasColorShell;
38
+ changeColorMode(useColor);
51
39
 
52
- const asError = o => as(t.red + t.bold, o);
53
- const asWarning = o => as(t.yellow, o);
54
- const asInfo = o => as(t.green, o);
55
- const asHelp = o => as(t.cyan, o);
56
- module.exports.underline = o => as(t.underline, o);
57
- module.exports.bold = o => as(t.bold, o);
58
-
59
- module.exports.asSeverity = (severity, msg) => {
60
- switch ((`${ severity }`).toLowerCase()) {
61
- case 'error': return asError(msg);
62
- case 'warning': return asWarning(msg);
63
- case 'info': return asInfo(msg);
64
- case 'help': return asHelp(msg);
65
- // or e.g. 'none'
66
- default: return msg;
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
- module.exports.codes = t;
71
- module.exports.as = as;
72
- module.exports.error = asError;
73
- module.exports.warn = asWarning;
74
- module.exports.info = asInfo;
75
- module.exports.help = asHelp;
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 };
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @class TimeTrace
7
7
  */
8
- class TimeTrace {
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
- let startTime;
17
- /**
18
- * Start measuring.
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
- * Stop measuring and log the result
30
- *
31
- * @param {number} indent
21
+ /**
22
+ * Start watch.
32
23
  */
33
- this.stop = function stop(indent) {
34
- const endTime = process.hrtime(startTime);
35
- const base = `${ ' '.repeat(indent * 2) }${ id } took:`;
36
- // eslint-disable-next-line no-console
37
- console.error( `${ base }${ ' '.repeat(60 - base.length) } %ds %dms`, endTime[0], endTime[1] / 1000000);
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 TimeTrace(id);
87
+ const b = new StopWatch(id);
71
88
  this.traceStack.push(b);
72
- b.start(this.traceStack.length - 1);
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(this.traceStack.length);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.7.0",
3
+ "version": "2.11.2",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",