@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.
- package/CHANGELOG.md +155 -1594
- package/bin/cdsc.js +144 -66
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +237 -122
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +12 -16
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +152 -37
- package/lib/base/messages.js +145 -83
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +19 -0
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +11 -32
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +4 -5
- package/lib/compiler/assert-consistency.js +16 -10
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +98 -9
- package/lib/compiler/checks.js +22 -70
- package/lib/compiler/define.js +61 -13
- package/lib/compiler/extend.js +79 -14
- package/lib/compiler/finalize-parse-cdl.js +46 -29
- package/lib/compiler/index.js +100 -37
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +19 -18
- package/lib/compiler/propagator.js +7 -4
- package/lib/compiler/resolve.js +297 -234
- package/lib/compiler/shared.js +107 -102
- package/lib/compiler/tweak-assocs.js +16 -11
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/annotations/genericTranslation.js +93 -21
- package/lib/edm/csn2edm.js +230 -115
- package/lib/edm/edm.js +305 -226
- package/lib/edm/edmPreprocessor.js +509 -438
- package/lib/edm/edmUtils.js +31 -45
- package/lib/gen/Dictionary.json +98 -22
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +10 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20786 -22199
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +59 -51
- package/lib/json/to-csn.js +10 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +62 -39
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +348 -229
- package/lib/language/language.g4 +629 -653
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +46 -43
- package/lib/main.js +108 -79
- package/lib/model/csnRefs.js +34 -7
- package/lib/model/csnUtils.js +337 -332
- package/lib/model/enrichCsn.js +1 -0
- package/lib/model/revealInternalProperties.js +30 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +6 -6
- package/lib/optionProcessor.js +73 -46
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +1042 -882
- package/lib/render/toHdbcds.js +195 -245
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +225 -241
- package/lib/render/utils/common.js +145 -15
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +4 -2
- package/lib/transform/db/expansion.js +22 -16
- package/lib/transform/db/flattening.js +109 -80
- package/lib/transform/db/transformExists.js +7 -7
- package/lib/transform/db/views.js +9 -6
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +62 -48
- package/lib/transform/forOdataNew.js +49 -50
- package/lib/transform/localized.js +31 -20
- package/lib/transform/odata/toFinalBaseType.js +16 -14
- package/lib/transform/odata/typesExposure.js +146 -198
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +67 -84
- package/lib/transform/translateAssocsToJoins.js +7 -3
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +16 -9
- package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -296
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
|
@@ -1,43 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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(
|
|
33
|
+
const lines = str.split(cdlNewLineRegEx);
|
|
62
34
|
const n = lines.length;
|
|
63
35
|
|
|
64
|
-
const hasTrailingLineBreak =
|
|
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 (
|
|
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 &&
|
|
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 (!
|
|
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() && /^[
|
|
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 (/^[
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const finalizeParseCdl =
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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 },
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
smartId,
|
|
128
|
-
smartFunctionId: smartFuncId,
|
|
129
|
-
delimitedId,
|
|
129
|
+
hdbcds: Object.assign((...args) => snapi.hdbcds(...args), {
|
|
130
|
+
keywords: Object.freeze([ ...keywords.hdbcds ] ),
|
|
130
131
|
}),
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
edm: Object.assign((...args) => snapi.edm(...args), {
|
|
133
|
+
all: (...args) => snapi.edm.all(...args)
|
|
133
134
|
}),
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
}
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
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 (
|
|
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)
|
|
551
|
-
|
|
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,
|
|
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
|
-
|
|
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 );
|