@sap/cds-compiler 2.13.8 → 3.0.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 (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -1,43 +1,15 @@
1
1
  'use strict';
2
2
 
3
- const whitespaceRegEx = /[\t\u{000B}\u{000C} \u{00A0}\u{FEFF}\p{Zs}]/u;
4
- const newLineRegEx = /\r\n?|\n|\u2028|\u2029/u;
5
-
6
- /**
7
- * Returns true if the given string only contains whitespace characters.
8
- *
9
- * @todo Combine with function from docCommentParser
10
- * @param {string} str
11
- * @returns {boolean}
12
- */
13
- function isWhiteSpaceOnly(str) {
14
- return /^\s*$/.test(str);
15
- }
16
-
17
- /**
18
- * Check whether the given character is a white-space character as
19
- * defined by §11.2 of the ECMAScript 2020 specification.
20
- * See <https://262.ecma-international.org/11.0/#sec-white-space>.
21
- *
22
- * | Code Point | Name | Abbreviation |
23
- * |:--------------------|:-----------------------------------------------|--------------|
24
- * | U+0009 | CHARACTER TABULATION | `<TAB>` |
25
- * | U+000B | LINE TABULATION | `<VT>` |
26
- * | U+000C | FORM FEED (FF) | `<FF>` |
27
- * | U+0020 | SPACE | `<SP>` |
28
- * | U+00A0 | NO-BREAK SPACE | `<NBSP>` |
29
- * | U+FEFF | ZERO WIDTH NO-BREAK SPACE | `<ZWNBSP>` |
30
- * | Other category “Zs” | Any other Unicode “Space_Separator” code point | `<USP>` |
31
- *
32
- * @param char
33
- * @returns {boolean}
34
- */
35
- function isWhitespaceCharacter(char) {
36
- return whitespaceRegEx.test(char);
37
- }
3
+ const {
4
+ isWhitespaceOrNewLineOnly,
5
+ isWhitespaceCharacterNoNewline,
6
+ cdlNewLineRegEx,
7
+ } = require('./textUtils');
38
8
 
39
9
  /**
40
10
  * Strips and counts the indentation from the given string.
11
+ * This function is similar to the one in docCommentParser.js, but
12
+ * has special handling for the first and last line of the string.
41
13
  *
42
14
  * @example
43
15
  * | hello
@@ -58,10 +30,10 @@ function stripIndentation(str) {
58
30
  }
59
31
 
60
32
  // Note: We have to check all newline characters, as the string is not normalized, yet.
61
- const lines = str.split(newLineRegEx);
33
+ const lines = str.split(cdlNewLineRegEx);
62
34
  const n = lines.length;
63
35
 
64
- const hasTrailingLineBreak = newLineRegEx.test(str[str.length - 1]);
36
+ const hasTrailingLineBreak = cdlNewLineRegEx.test(str[str.length - 1]);
65
37
  if (hasTrailingLineBreak) {
66
38
  // Shortcut:
67
39
  // If there is a trailing line break, it means that ``` is on newline and
@@ -73,12 +45,12 @@ function stripIndentation(str) {
73
45
  const minIndent = lines.reduce((min, line, index) => {
74
46
  // Note: Last line is the line containing ```. There, we always count the indentation,
75
47
  // even if blank. For all other lines, blank lines are ignored.
76
- if (isWhiteSpaceOnly(line) && index !== (n-1))
48
+ if (isWhitespaceOrNewLineOnly(line) && index !== (n-1))
77
49
  return min;
78
50
 
79
51
  let count = 0;
80
52
  const length = Math.min(min, line.length);
81
- while (count < length && isWhitespaceCharacter(line[count])) {
53
+ while (count < length && isWhitespaceCharacterNoNewline(line[count])) {
82
54
  count++;
83
55
  }
84
56
  return Math.min(min, count);
@@ -138,7 +110,7 @@ class MultiLineStringParser {
138
110
  // If there are no line breaks, emit an error as normal single-back-tick
139
111
  // strings should be used instead. Because the first line is skipped,
140
112
  // there is no text without at least one line break.
141
- if (!newLineRegEx.test(this.str)) {
113
+ if (!cdlNewLineRegEx.test(this.str)) {
142
114
  const loc = this._locationForCharacters(this.end, 1);
143
115
  this.parser.message('syntax-invalid-text-block', loc);
144
116
  return '';
@@ -300,7 +272,7 @@ class MultiLineStringParser {
300
272
  let codePoint = '';
301
273
 
302
274
  for (let j = 0; j < count; ++j) {
303
- if (!this._eos() && /^[0-9A-Fa-f]$/.test(this._lookahead())) {
275
+ if (!this._eos() && /^[\dA-Fa-f]$/.test(this._lookahead())) {
304
276
  this._move();
305
277
  codePoint += this._current();
306
278
  } else {
@@ -330,7 +302,7 @@ class MultiLineStringParser {
330
302
  this._move(); // 'u'
331
303
 
332
304
  while (!this._eos()) {
333
- if (/^[0-9A-Fa-f]$/.test(this._lookahead())) {
305
+ if (/^[\dA-Fa-f]$/.test(this._lookahead())) {
334
306
  this._move();
335
307
  codePoint += this._current();
336
308
  } else if (this._lookahead() === '}') {
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /** Whitespace characters without line-breaks. */
4
+ const whitespaceRegEx = /[\t\u{000B}\u{000C} \u{00A0}\u{FEFF}\p{Zs}]/u;
5
+ const cdlNewLineRegEx = /\r\n?|\n|\u2028|\u2029/u;
6
+
7
+ /**
8
+ * Returns true if the given string only contains whitespace characters.
9
+ * In contrast to `whitespaceRegEx`, it also matches newlines.
10
+ *
11
+ * @param {string} str
12
+ * @returns {boolean}
13
+ */
14
+ function isWhitespaceOrNewLineOnly(str) {
15
+ return /^\s*$/.test(str);
16
+ }
17
+
18
+ /**
19
+ * Check whether the given character is a white-space character as
20
+ * defined by §11.2 of the ECMAScript 2020 specification.
21
+ * See <https://262.ecma-international.org/11.0/#sec-white-space>.
22
+ *
23
+ * | Code Point | Name | Abbreviation |
24
+ * |:--------------------|:-----------------------------------------------|--------------|
25
+ * | U+0009 | CHARACTER TABULATION | `<TAB>` |
26
+ * | U+000B | LINE TABULATION | `<VT>` |
27
+ * | U+000C | FORM FEED (FF) | `<FF>` |
28
+ * | U+0020 | SPACE | `<SP>` |
29
+ * | U+00A0 | NO-BREAK SPACE | `<NBSP>` |
30
+ * | U+FEFF | ZERO WIDTH NO-BREAK SPACE | `<ZWNBSP>` |
31
+ * | Other category “Zs” | Any other Unicode “Space_Separator” code point | `<USP>` |
32
+ *
33
+ * @param char
34
+ * @returns {boolean}
35
+ */
36
+ function isWhitespaceCharacterNoNewline(char) {
37
+ return whitespaceRegEx.test(char);
38
+ }
39
+
40
+ module.exports = {
41
+ isWhitespaceOrNewLineOnly,
42
+ isWhitespaceCharacterNoNewline,
43
+ cdlNewLineRegEx,
44
+ };
package/lib/main.d.ts CHANGED
@@ -81,6 +81,20 @@ declare namespace compiler {
81
81
  * use this frontend as the fallback parser.
82
82
  */
83
83
  fallbackParser?: string | 'cdl' | 'csn'
84
+ /**
85
+ * Where to find `@sap/cds/` packages. This string, if set, is used as
86
+ * the prefix for SAP CDS packages / CDS files.
87
+ */
88
+ cdsHome?: string
89
+ /**
90
+ * When set to `true`, and the model contains an entity `sap.common.Languages`
91
+ * with an element `code`, all generated texts entities additionally contain
92
+ * an element `language` which is an association to `sap.common.Languages`
93
+ * using element `locale`.
94
+ *
95
+ * @since v2.8.0
96
+ */
97
+ addTextsLanguageAssoc?: boolean
84
98
  /**
85
99
  * Option for {@link compileSources}. If set, all objects inside the
86
100
  * provided sources dictionary are interpreted as XSN structures instead
@@ -398,6 +412,10 @@ declare namespace compiler {
398
412
  * It could also be a simple string, which is then considered to be the source
399
413
  * text of a file named `<stdin>.cds`.
400
414
  *
415
+ * This function uses the direct value of USINGs (in CSN `"requires"`) for its dependency graph,
416
+ * i.e. this function does not resolve USINGs. If a USING matches a key in sourcesDict,
417
+ * we assume that it depends on that file (/sourcesDict entry).
418
+ *
401
419
  * See function {@link compile} for the meaning of the argument `options`. If there
402
420
  * are parse or other compilation errors, throws an exception {@link CompilationError}
403
421
  * containing a list of individual errors.
@@ -576,7 +594,6 @@ declare namespace compiler {
576
594
  * coloring will be used. If 'auto', we will decide based on certain factors such
577
595
  * as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
578
596
  * unset.
579
-
580
597
  */
581
598
  export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
582
599
  color?: boolean | 'auto'
@@ -603,25 +620,6 @@ declare namespace compiler {
603
620
  */
604
621
  export function hasErrors(messages: CompileMessage[]): boolean;
605
622
 
606
- /**
607
- * Same as {@link to.edm} but expects a {@link for.odata} transformed CSN.
608
- *
609
- * Note that parameter `service` is the same as `ODataOptions.service`.
610
- * The latter is not used. OData specific options have an internal format.
611
- *
612
- * @deprecated Use {@link to.edm} instead. If it detects a pre-transformed CSN, it won't run for.odata().
613
- */
614
- export function preparedCsnToEdm(csn: CSN, service: string, options: ODataOptions): object;
615
- /**
616
- * Same as {@link to.edm} but expects a {@link for.odata} transformed CSN.
617
- *
618
- * Note that parameter `service` is the same as `ODataOptions.service`.
619
- * The latter is not used. OData specific options have an outdated format.
620
- *
621
- * @deprecated Use {@link to.edmx} instead. If it detects a pre-transformed CSN, it won't run for.odata().
622
- */
623
- export function preparedCsnToEdmx(csn: CSN, service: string, options: ODataOptions): string;
624
-
625
623
  export namespace parse {
626
624
  /**
627
625
  * Parse the given CDL in parseCdl mode and return its corresponding CSN representation.
@@ -653,15 +651,6 @@ declare namespace compiler {
653
651
  function expr(cdl: string, filename?: string, options?: Options): CXN;
654
652
  }
655
653
 
656
- /**
657
- * @deprecated Use {@link parse.cql} instead.
658
- */
659
- export function parseToCqn(cql: string, filename?: string, options?: Options): CQN;
660
- /**
661
- * @deprecated Use {@link parse.expr} instead.
662
- */
663
- export function parseToExpr(expr: string, filename?: string, options?: Options): CXN;
664
-
665
654
  /**
666
655
  * @note Actual name is "for" which can't be used directly in the documentation
667
656
  * as it is a reserved name in TypeScript.
@@ -807,7 +796,7 @@ declare namespace compiler {
807
796
  /**
808
797
  * Rendered csn.namespace property + using directives.
809
798
  */
810
- [namespace: string]: string
799
+ namespace?: string
811
800
  }
812
801
 
813
802
  /**
@@ -852,20 +841,17 @@ declare namespace compiler {
852
841
  * - For the 'plain' naming mode, it means converting all `.` to `_` and upper-casing.
853
842
  * - For the 'quoted' naming mode, this means correctly replacing some `.` with `_`.
854
843
  *
844
+ * The above rules might differ for different SQL dialects.
845
+ * Exceptions will be listed below.
846
+ *
855
847
  * @param artifactName The fully qualified name of the artifact
856
848
  * @param sqlMapping The naming mode to use. See {@link SqlOptions.sqlMapping} for more details.
857
849
  * @param csn
850
+ * @param sqlDialect The SQL dialect to use. See {@link SqlOptions.sqlDialect} for more details.
858
851
  * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
859
852
  * @since v2.1.0
860
853
  */
861
- export function getArtifactCdsPersistenceName(artifactName: string, sqlMapping: string, csn: CSN): string;
862
- /**
863
- * This is an old function signature. If it is used - with a namespace as the third argument - the result might be wrong,
864
- * since the `.` -> `_` conversion for 'quoted'/'hdbcds' is missing.
865
- *
866
- * @deprecated Use the other overload with CSN instead.
867
- */
868
- export function getArtifactCdsPersistenceName(artifactName: string, sqlMapping: string, namespace: string): string;
854
+ export function getArtifactCdsPersistenceName(artifactName: string, sqlMapping: string, csn: CSN, sqlDialect: string): string;
869
855
  /**
870
856
  * Return the resulting database element name for `elemName`, depending on the current
871
857
  * naming mode.
@@ -874,11 +860,15 @@ declare namespace compiler {
874
860
  * - For the 'quoted' naming mode, it means converting all `.` to `_`.
875
861
  * No other naming modes are accepted!
876
862
  *
863
+ * The above rules might differ for different SQL dialects.
864
+ * Exceptions will be listed below.
865
+ *
877
866
  * @param elemName The name of the element. For structured elements, concat by dot, e.g. `sub.elem`.
878
867
  * @param sqlMapping The naming mode to use. See {@link SqlOptions.sqlMapping} for more details.
868
+ * @param sqlDialect The SQL dialect to use. See {@link SqlOptions.sqlDialect} for more details.
879
869
  * @returns The resulting database element name for 'elemName', depending on the current naming mode.
880
870
  */
881
- export function getElementCdsPersistenceName(elemName: string, sqlMapping: string): string;
871
+ export function getElementCdsPersistenceName(elemName: string, sqlMapping: string, sqlDialect: string): string;
882
872
 
883
873
  /**
884
874
  * Traverse the CSN node `csn`.
@@ -895,7 +885,20 @@ declare namespace compiler {
895
885
  * The functions in `userFunctions` are usually transformer functions, which
896
886
  * change the input CSN destructively.
897
887
  */
898
- export function traverseCsn(userFunctions: Record<string, Function>, csn: object|any[]);
888
+ export function traverseCsn(userFunctions: Record<string, Function>, csn: object|any[]): void;
889
+
890
+ /**
891
+ * CSN Model related functions.
892
+ */
893
+ export namespace model {
894
+ /**
895
+ * Returns true if the given definition name is in a reserved namespace such as `cds.*`
896
+ * but not `cds.foundation.*`.
897
+ *
898
+ * @param definitionName Top-level definition name of the artifact.
899
+ */
900
+ function isInReservedNamespace(definitionName: string): boolean;
901
+ }
899
902
 
900
903
  /**
901
904
  * @private
@@ -985,7 +988,7 @@ declare namespace compiler {
985
988
  }
986
989
  }
987
990
  /**
988
- * String representation of the message. May be a multi-line message in the future.
991
+ * String representation of the message. It may be a multi-line message in the future.
989
992
  */
990
993
  message: string
991
994
  /**
@@ -1004,7 +1007,7 @@ declare namespace compiler {
1004
1007
  */
1005
1008
  error?: Error
1006
1009
  /**
1007
- * Returns a human readable string of the compiler message. Uses {@link messageString} to render
1010
+ * Returns a human-readable string of the compiler message. Uses {@link messageString} to render
1008
1011
  * the message without filename normalization and without a message ID.
1009
1012
  */
1010
1013
  toString(): string;
@@ -1036,7 +1039,7 @@ declare namespace compiler {
1036
1039
  * - `false`: the file does not exist
1037
1040
  * - `true`: file exists (fstat), no further knowledge yet - i.e. value will change!
1038
1041
  * - `string` or `Buffer`: the file content
1039
- * - `{ realname: fs.realpath(filename) }`: if filename is not canonicalized
1042
+ * - `{ realname: fs.realpath.native(filename) }`: if filename is not canonicalized
1040
1043
  */
1041
1044
  export type FileCache = Record<string, boolean | string | Buffer | { realname: string }>;
1042
1045
 
package/lib/main.js CHANGED
@@ -13,69 +13,59 @@
13
13
 
14
14
  'use strict';
15
15
 
16
- const backends = require('./backends');
17
- const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
18
- const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
19
- const { traverseCsn } = require('./model/api');
20
- const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
21
- const { smartId, smartFuncId, delimitedId } = require('./sql-identifier');
22
- const keywords = require( './base/keywords' );
23
-
24
- const parseLanguage = require('./language/antlrParser');
25
- const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
26
- const { fns } = require('./compiler/shared');
27
- const define = require('./compiler/define');
28
- const finalizeParseCdl = require('./compiler/finalize-parse-cdl');
16
+ const snapi = lazyload('./api/main');
17
+ const csnUtils = lazyload('./model/csnUtils');
18
+ const model_api = lazyload('./model/api');
19
+ const messages = lazyload('./base/messages');
20
+ const sqlIdentifier = lazyload('./sql-identifier');
21
+ const keywords = lazyload( './base/keywords' );
22
+
23
+ const parseLanguage = lazyload('./language/antlrParser');
24
+ const compiler = lazyload('./compiler');
25
+ const shared = lazyload('./compiler/shared');
26
+ const define = lazyload('./compiler/define');
27
+ const builtins = lazyload("./compiler/builtins");
28
+ const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
29
29
 
30
30
  // The compiler version (taken from package.json)
31
31
  function version() {
32
32
  return require('../package.json').version;
33
33
  }
34
34
 
35
- const {
36
- CompilationError,
37
- messageString,
38
- messageStringMultiline,
39
- messageContext,
40
- hasErrors,
41
- explainMessage,
42
- hasMessageExplanation
43
- } = require('./base/messages');
44
-
45
- const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
35
+ const toCsn = lazyload('./json/to-csn')
46
36
 
47
37
  function parseCdl( cdlSource, filename, options = {} ) {
48
38
  options = Object.assign( {}, options, { parseCdl: true } );
49
39
  const sources = Object.create(null);
50
40
  /** @type {XSN.Model} */
51
41
  const model = { sources, options, $functions: {}, $volatileFunctions: {} };
52
- const messageFunctions = createMessageFunctions( options, 'parse', model );
42
+ const messageFunctions = messages.createMessageFunctions( options, 'parse', model );
53
43
  model.$messageFunctions = messageFunctions;
54
44
 
55
45
  const xsn = parseLanguage( cdlSource, filename, Object.assign( { parseOnly: true }, options ),
56
46
  messageFunctions );
57
47
  sources[filename] = xsn;
58
- fns( model );
48
+ shared.fns( model );
59
49
  define( model );
60
50
  finalizeParseCdl( model );
61
51
  messageFunctions.throwWithError();
62
- return compactModel( model );
52
+ return toCsn.compactModel( model );
63
53
  }
64
54
 
65
55
  function parseCql( cdlSource, filename = '<query>.cds', options = {} ) {
66
- const messageFunctions = createMessageFunctions( options, 'parse' );
56
+ const messageFunctions = messages.createMessageFunctions( options, 'parse' );
67
57
  const xsn = parseLanguage( cdlSource, filename, Object.assign( { parseOnly: true }, options ),
68
58
  messageFunctions, 'query' );
69
59
  messageFunctions.throwWithError();
70
- return compactQuery( xsn );
60
+ return toCsn.compactQuery( xsn );
71
61
  }
72
62
 
73
63
  function parseExpr( cdlSource, filename = '<expr>.cds', options = {} ) {
74
- const messageFunctions = createMessageFunctions( options, 'parse' );
64
+ const messageFunctions = messages.createMessageFunctions( options, 'parse' );
75
65
  const xsn = parseLanguage( cdlSource, filename, Object.assign( { parseOnly: true }, options ),
76
66
  messageFunctions, 'expr' );
77
67
  messageFunctions.throwWithError();
78
- return compactExpr( xsn );
68
+ return toCsn.compactExpr( xsn );
79
69
  }
80
70
 
81
71
  // FIXME: The implementation of those functions that delegate to 'backends' should probably move here
@@ -83,69 +73,108 @@ function parseExpr( cdlSource, filename = '<expr>.cds', options = {} ) {
83
73
  module.exports = {
84
74
  // Compiler
85
75
  version,
86
- compile: (...args) => compileX(...args).then( compactModel ), // main function
87
- compileSync: (filenames, dir, options, fileCache) => compactModel( compileSyncX(filenames, dir, options, fileCache) ), // main function
88
- compileSources: (sourcesDict, options) => compactModel( compileSourcesX(sourcesDict, options) ), // main function
76
+ compile: (...args) => compiler.compileX(...args).then( toCsn.compactModel ), // main function
77
+ compileSync: (filenames, dir, options, fileCache) => toCsn.compactModel( compiler.compileSyncX(filenames, dir, options, fileCache) ), // main function
78
+ compileSources: (sourcesDict, options) => toCsn.compactModel( compiler.compileSourcesX(sourcesDict, options) ), // main function
89
79
  compactModel: csn => csn, // for easy v2 migration
90
- CompilationError,
91
- sortMessages,
92
- sortMessagesSeverityAware,
93
- deduplicateMessages,
94
- messageString,
95
- messageStringMultiline,
96
- messageContext,
97
- explainMessage,
98
- hasMessageExplanation,
99
- InvocationError, // TODO: make it no error if same file name is provided twice
100
- hasErrors,
101
-
102
- // Backends
103
- // TODO: Expose when transformers are switched to CSN
104
- // toOdataWithCsn: backends.toOdataWithCsn,
105
- preparedCsnToEdmx : (csn, service, options) => { return backends.preparedCsnToEdmx(csn, service, options).edmx},
106
- preparedCsnToEdm : (csn, service, options) => { return backends.preparedCsnToEdm(csn, service, options).edmj},
80
+ get CompilationError() {
81
+ Object.defineProperty(this, "CompilationError", {
82
+ value: messages.CompilationError,
83
+ writable: false,
84
+ configurable: false,
85
+ enumerable: false
86
+ });
87
+ return messages.CompilationError;
88
+ },
89
+ sortMessages: (...args) => messages.sortMessages(...args),
90
+ sortMessagesSeverityAware: (...args) => messages.sortMessagesSeverityAware(...args),
91
+ deduplicateMessages: (...args) => messages.deduplicateMessages(...args),
92
+ messageString: (...args) => messages.messageString(...args),
93
+ messageStringMultiline: (...args) => messages.messageStringMultiline(...args),
94
+ messageContext: (...args) => messages.messageContext(...args),
95
+ explainMessage: (...args) => messages.explainMessage(...args),
96
+ hasMessageExplanation: (...args) => messages.hasMessageExplanation(...args),
97
+ get InvocationError() {
98
+ Object.defineProperty(this, "InvocationError", {
99
+ value: compiler.InvocationError,
100
+ writable: false,
101
+ configurable: false,
102
+ enumerable: false
103
+ });
104
+ return compiler.InvocationError;
105
+ },
106
+ hasErrors: (...args) => messages.hasErrors(...args),
107
107
 
108
108
  // additional API:
109
- parse: { cdl: parseCdl, cql: parseCql, expr: parseExpr }, // preferred names
110
- /**
111
- * @deprecated Use parse.cql instead
112
- */
113
- parseToCqn: parseCql,
114
- /**
115
- * @deprecated Use parse.expr instead
116
- */
117
- parseToExpr: parseExpr, // deprecated name
109
+ parse: { cdl: (...args) => parseCdl(...args), cql: (...args) => parseCql(...args), expr: (...args) => parseExpr(...args) },
118
110
  // SNAPI
119
- for: { odata },
111
+ for: { odata: (...args) => snapi.odata(...args) },
120
112
  to: {
121
- cdl: Object.assign(cdl, {
122
- keywords: Object.freeze([ ...keywords.cdl ]),
123
- functions: Object.freeze([ ...keywords.cdl_functions ]),
113
+ cdl: Object.assign((...args) => snapi.cdl(...args), {
114
+ keywords: Object.freeze([ ...keywords.cdl ] ),
115
+ functions: Object.freeze([ ...keywords.cdl_functions ] ),
116
+ }),
117
+ sql: Object.assign((...args) => snapi.sql(...args), {
118
+ sqlite: {
119
+ keywords: Object.freeze([ ...keywords.sqlite ] )
120
+ },
121
+ smartId: (...args) => sqlIdentifier.smartId(...args),
122
+ smartFunctionId: (...args) => sqlIdentifier.smartFuncId(...args),
123
+ delimitedId: (...args) => sqlIdentifier.delimitedId(...args),
124
+ }),
125
+ hdi: Object.assign((...args) => snapi.hdi(...args), {
126
+ migration: (...args) => snapi.hdi.migration(...args),
127
+ keywords: Object.freeze([ ...keywords.hana ] ),
124
128
  }),
125
- sql: Object.assign(sql, {
126
- sqlite: { keywords: Object.freeze([ ...keywords.sqlite ] )},
127
- smartId,
128
- smartFunctionId: smartFuncId,
129
- delimitedId,
129
+ hdbcds: Object.assign((...args) => snapi.hdbcds(...args), {
130
+ keywords: Object.freeze([ ...keywords.hdbcds ] ),
130
131
  }),
131
- hdi: Object.assign(hdi, {
132
- keywords: Object.freeze([ ...keywords.hana ]),
132
+ edm: Object.assign((...args) => snapi.edm(...args), {
133
+ all: (...args) => snapi.edm.all(...args)
133
134
  }),
134
- hdbcds: Object.assign(hdbcds, {
135
- keywords: Object.freeze([ ...keywords.hdbcds ]),
135
+ edmx: Object.assign((...args) => snapi.edmx(...args), {
136
+ all: (...args) => snapi.edmx.all(...args)
136
137
  }),
137
- edm,
138
- edmx
139
138
  },
140
139
  // Convenience for hdbtabledata calculation in @sap/cds
141
- getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
142
- getElementCdsPersistenceName: getElementDatabaseNameOf,
140
+ getArtifactCdsPersistenceName: (...args) => csnUtils.getArtifactDatabaseNameOf(...args),
141
+ getElementCdsPersistenceName: (...args) => csnUtils.getElementDatabaseNameOf(...args),
143
142
 
144
143
  // Other API functions:
145
- traverseCsn,
144
+ traverseCsn: (...args) => model_api.traverseCsn(...args),
146
145
 
147
146
  // INTERNAL functions for the cds-lsp package and friends - before you use
148
147
  // it, you MUST talk with us - there can be potential incompatibilities with
149
148
  // new releases (even having the same major version):
150
- $lsp: { parse: parseX, compile: compileX, getArtifactName: a => a.name },
149
+ $lsp: { parse: (...args) => compiler.parseX(...args), compile: (...args) => compiler.compileX(...args), getArtifactName: a => a.name },
150
+
151
+ // CSN Model related functionality
152
+ model: {
153
+ isInReservedNamespace: (...args) => builtins.isInReservedNamespace(...args),
154
+ },
151
155
  };
156
+
157
+ /**
158
+ * Load the module on-demand and not immediately.
159
+ *
160
+ * @param {string} moduleName Name of the module to load - like with require
161
+ * @returns {object} A Proxy that handles the on-demand loading
162
+ */
163
+ function lazyload(moduleName) {
164
+ let module;
165
+ return new Proxy(((...args) => {
166
+ if (!module) // eslint-disable-next-line global-require
167
+ module = require(moduleName);
168
+
169
+ if (module.apply && typeof module.apply === 'function')
170
+ return module.apply(this, args);
171
+ return module; // for destructured calls
172
+ }), {
173
+ get(target, name) {
174
+ if (!module) // eslint-disable-next-line global-require
175
+ module = require(moduleName);
176
+
177
+ return module[name];
178
+ },
179
+ });
180
+ }
@@ -327,7 +327,7 @@ function csnRefs( csn, universalReady ) {
327
327
  // Backend bug workaround, TODO: delete next 2 lines
328
328
  if (notFound !== undefined)
329
329
  return notFound;
330
- throw new ModelError( 'Undefined reference' );
330
+ throw new ModelError( `Unknown artifact reference: ${typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref}` );
331
331
  }
332
332
 
333
333
  function artifactPathRef( ref ) {
@@ -442,7 +442,11 @@ function csnRefs( csn, universalReady ) {
442
442
  const queries = cached( main, '$queries', allQueries );
443
443
  for (const qcache of queries || []) {
444
444
  const { _select } = qcache;
445
- traverseType( _select, main, null, null, initNode ); // also inits elements
445
+ const { elements } = _select;
446
+ if (elements) {
447
+ for (const n of Object.keys( elements ))
448
+ traverseDef( elements[n], _select, 'element', n, initNode );
449
+ }
446
450
  if (_select.mixin) {
447
451
  for (const n of Object.keys( _select.mixin ))
448
452
  setCache( _select.mixin[n], '_parent', _select ); // relevant initNode() part
@@ -453,7 +457,13 @@ function csnRefs( csn, universalReady ) {
453
457
 
454
458
  function initNode( art, parent, kind, name ) {
455
459
  setCache( art, '_parent', parent );
456
- if (art.type || !kind || kind === 'target') // with type, top-level, query or mixin
460
+ if (kind === 'target') {
461
+ // Prevent re-initialization of anonymous aspect with initDefinition():
462
+ // (that would be with parent: null which would be wrong)
463
+ setCache( art, '$queries', null );
464
+ return;
465
+ }
466
+ if (art.type || !kind) // with type, top-level, query or mixin
457
467
  return;
458
468
  const { $origin } = art;
459
469
  if (typeof $origin === 'object') // null, […], {…}
@@ -547,8 +557,19 @@ function csnRefs( csn, universalReady ) {
547
557
  }
548
558
  if (baseEnv) // ref-target (filter condition), expand, inline
549
559
  return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
550
- if (!query) // outside queries - TODO: items?
551
- return resolvePath( path, parent.elements[head], parent, 'parent' );
560
+ if (!query) { // outside queries - TODO: items?
561
+ let art = parent.elements[head];
562
+ // Ref to up_ in anonymous aspect
563
+ if (!art && head === 'up_') {
564
+ const up = getCache( parent, '_parent' );
565
+ const target = up && typeof up.target === 'string' && csn.definitions[up.target];
566
+ if (target && target.elements) {
567
+ initDefinition( target );
568
+ art = target.elements.up_;
569
+ }
570
+ }
571
+ return resolvePath( path, art, parent, 'parent' );
572
+ }
552
573
 
553
574
  if (semantics.dynamic === 'query')
554
575
  // TODO: for ON condition in expand, would need to use cached _element
@@ -703,6 +724,11 @@ function csnRefs( csn, universalReady ) {
703
724
  hidden = {};
704
725
  cache.set( obj, hidden );
705
726
  }
727
+ // TODO: we might keep the following with --test-mode
728
+ // if (hidden[prop] !== undefined) {
729
+ // console.log('RS:',prop,hidden[prop],val,obj)
730
+ // throw Error('RESET')
731
+ // }
706
732
  hidden[prop] = val;
707
733
  return val;
708
734
  }
@@ -818,7 +844,7 @@ function traverseDef( node, parent, kind, name, callback ) {
818
844
  }
819
845
  if (node.returns)
820
846
  traverseType( node.returns, node, 'returns', true, callback );
821
- traverseType( node, parent, kind, name, callback );
847
+ traverseType( node, true, kind, name, callback );
822
848
  if (node.actions) {
823
849
  for (const n of Object.keys( node.actions ))
824
850
  traverseDef( node.actions[n], node, 'action', n, callback )
@@ -826,7 +852,8 @@ function traverseDef( node, parent, kind, name, callback ) {
826
852
  }
827
853
 
828
854
  function traverseType( node, parent, kind, name, callback ) {
829
- callback ( node, parent, kind, name );
855
+ if (parent !== true)
856
+ callback ( node, parent, kind, name );
830
857
  const target = targetAspect( node );
831
858
  if (target && typeof target === 'object' && target.elements) {
832
859
  callback ( target, node, 'target', true );