@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.
Files changed (103) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +58 -35
  5. package/bin/cdsse.js +1 -0
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +16 -0
  9. package/lib/api/.eslintrc.json +2 -0
  10. package/lib/api/main.js +10 -36
  11. package/lib/api/options.js +17 -8
  12. package/lib/api/validate.js +30 -3
  13. package/lib/backends.js +12 -13
  14. package/lib/base/dictionaries.js +2 -1
  15. package/lib/base/keywords.js +3 -2
  16. package/lib/base/message-registry.js +64 -11
  17. package/lib/base/messages.js +38 -18
  18. package/lib/base/model.js +6 -4
  19. package/lib/base/optionProcessorHelper.js +148 -86
  20. package/lib/checks/.eslintrc.json +2 -0
  21. package/lib/checks/actionsFunctions.js +2 -1
  22. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  23. package/lib/checks/foreignKeys.js +4 -4
  24. package/lib/checks/managedInType.js +4 -4
  25. package/lib/checks/queryNoDbArtifacts.js +1 -3
  26. package/lib/checks/selectItems.js +4 -0
  27. package/lib/checks/sql-snippets.js +93 -0
  28. package/lib/checks/unknownMagic.js +6 -3
  29. package/lib/checks/validator.js +8 -0
  30. package/lib/compiler/assert-consistency.js +14 -5
  31. package/lib/compiler/base.js +64 -0
  32. package/lib/compiler/builtins.js +62 -16
  33. package/lib/compiler/checks.js +34 -10
  34. package/lib/compiler/definer.js +91 -112
  35. package/lib/compiler/index.js +30 -30
  36. package/lib/compiler/propagator.js +8 -4
  37. package/lib/compiler/resolver.js +279 -63
  38. package/lib/compiler/shared.js +65 -230
  39. package/lib/compiler/utils.js +191 -0
  40. package/lib/edm/annotations/genericTranslation.js +35 -18
  41. package/lib/edm/annotations/preprocessAnnotations.js +1 -1
  42. package/lib/edm/csn2edm.js +4 -3
  43. package/lib/edm/edm.js +8 -8
  44. package/lib/edm/edmPreprocessor.js +61 -59
  45. package/lib/edm/edmUtils.js +14 -15
  46. package/lib/gen/Dictionary.json +82 -40
  47. package/lib/gen/language.checksum +1 -1
  48. package/lib/gen/language.interp +19 -1
  49. package/lib/gen/language.tokens +80 -73
  50. package/lib/gen/languageLexer.interp +27 -1
  51. package/lib/gen/languageLexer.js +925 -826
  52. package/lib/gen/languageLexer.tokens +72 -65
  53. package/lib/gen/languageParser.js +4817 -4102
  54. package/lib/json/from-csn.js +57 -26
  55. package/lib/json/to-csn.js +244 -51
  56. package/lib/language/antlrParser.js +12 -1
  57. package/lib/language/docCommentParser.js +1 -1
  58. package/lib/language/errorStrategy.js +26 -8
  59. package/lib/language/genericAntlrParser.js +106 -30
  60. package/lib/language/language.g4 +200 -70
  61. package/lib/language/multiLineStringParser.js +536 -0
  62. package/lib/main.d.ts +220 -21
  63. package/lib/main.js +6 -3
  64. package/lib/model/api.js +2 -2
  65. package/lib/model/csnRefs.js +218 -86
  66. package/lib/model/csnUtils.js +99 -178
  67. package/lib/model/enrichCsn.js +84 -43
  68. package/lib/model/revealInternalProperties.js +25 -8
  69. package/lib/model/sortViews.js +8 -1
  70. package/lib/modelCompare/compare.js +2 -1
  71. package/lib/optionProcessor.js +33 -18
  72. package/lib/render/.eslintrc.json +1 -2
  73. package/lib/render/DuplicateChecker.js +2 -2
  74. package/lib/render/manageConstraints.js +1 -1
  75. package/lib/render/toCdl.js +202 -82
  76. package/lib/render/toHdbcds.js +194 -135
  77. package/lib/render/toRename.js +7 -10
  78. package/lib/render/toSql.js +91 -51
  79. package/lib/render/utils/common.js +24 -5
  80. package/lib/render/utils/sql.js +6 -4
  81. package/lib/transform/braceExpression.js +4 -2
  82. package/lib/transform/db/applyTransformations.js +189 -0
  83. package/lib/transform/db/associations.js +389 -0
  84. package/lib/transform/db/cdsPersistence.js +150 -0
  85. package/lib/transform/db/constraints.js +275 -119
  86. package/lib/transform/db/draft.js +6 -4
  87. package/lib/transform/db/expansion.js +10 -9
  88. package/lib/transform/db/flattening.js +23 -8
  89. package/lib/transform/db/temporal.js +236 -0
  90. package/lib/transform/db/transformExists.js +106 -25
  91. package/lib/transform/db/views.js +485 -0
  92. package/lib/transform/forHanaNew.js +90 -1036
  93. package/lib/transform/forOdataNew.js +11 -3
  94. package/lib/transform/localized.js +5 -14
  95. package/lib/transform/odata/generateForeignKeyElements.js +2 -2
  96. package/lib/transform/transformUtilsNew.js +34 -20
  97. package/lib/transform/translateAssocsToJoins.js +15 -23
  98. package/lib/transform/universalCsnEnricher.js +217 -47
  99. package/lib/utils/file.js +13 -6
  100. package/lib/utils/term.js +65 -42
  101. package/lib/utils/timetrace.js +55 -27
  102. package/package.json +1 -1
  103. package/lib/transform/db/helpers.js +0 -58
@@ -1,67 +1,237 @@
1
1
  'use strict';
2
2
 
3
- const { forEachDefinition } = require('../base/model');
3
+ const { setProp } = require('../base/model');
4
4
  const {
5
- applyTransformations,
6
- cloneCsn,
5
+ forEachDefinition,
6
+ forAllQueries,
7
7
  getUtils,
8
- isBuiltinType,
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, 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);
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
- // 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);
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
- }, 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;
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
- 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;
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
- // TODO: check if this works fine for items/returns/actions
65
- construct[memberName] = newMember;
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 und Macintosh line breaks.
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 as well, which is executed
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: string, cb: (err, data) => void) => void} reader
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 instanceof Error) {
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. 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 };
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * A single TimeTrace encapsulates the runtime of a selected code frame.
4
+ * A single StopWatch encapsulates the runtime of a selected code frame.
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,48 @@ 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
+ // 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
- * Stop measuring and log the result
30
- *
31
- * @param {number} indent
23
+ /**
24
+ * Start watch.
32
25
  */
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
- };
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 TimeTrace(id);
89
+ const b = new StopWatch(id);
71
90
  this.traceStack.push(b);
72
- b.start(this.traceStack.length - 1);
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(this.traceStack.length);
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 = doTimeTrace ? new TimeTracer() : ignoreTimeTrace;
128
+ module.exports = {
129
+ timetrace: (doTimeTrace ? new TimeTracer() : ignoreTimeTrace),
130
+ TimeTracer,
131
+ StopWatch,
132
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.10.4",
3
+ "version": "2.12.0",
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)",
@@ -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
- };