@sap/cds-compiler 5.9.4 → 6.0.12

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 (114) hide show
  1. package/CHANGELOG.md +117 -319
  2. package/README.md +1 -1
  3. package/bin/cds_update_identifiers.js +3 -5
  4. package/bin/cdsc.js +24 -9
  5. package/bin/cdshi.js +1 -1
  6. package/bin/cdsse.js +4 -4
  7. package/doc/CHANGELOG_BETA.md +11 -0
  8. package/doc/CHANGELOG_DEPRECATED.md +29 -0
  9. package/lib/api/main.js +8 -5
  10. package/lib/api/options.js +12 -10
  11. package/lib/base/builtins.js +1 -0
  12. package/lib/base/message-registry.js +191 -99
  13. package/lib/base/messages.js +35 -21
  14. package/lib/base/model.js +14 -24
  15. package/lib/checks/actionsFunctions.js +10 -20
  16. package/lib/checks/annotationsOData.js +1 -1
  17. package/lib/checks/elements.js +35 -10
  18. package/lib/checks/enums.js +31 -0
  19. package/lib/checks/foreignKeys.js +2 -2
  20. package/lib/checks/hasPersistedElements.js +5 -0
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedWithoutKeys.js +5 -4
  23. package/lib/checks/queryNoDbArtifacts.js +10 -8
  24. package/lib/checks/types.js +5 -5
  25. package/lib/checks/validator.js +6 -4
  26. package/lib/compiler/assert-consistency.js +13 -9
  27. package/lib/compiler/checks.js +20 -52
  28. package/lib/compiler/define.js +31 -6
  29. package/lib/compiler/extend.js +5 -1
  30. package/lib/compiler/generate.js +14 -17
  31. package/lib/compiler/populate.js +8 -31
  32. package/lib/compiler/propagator.js +21 -35
  33. package/lib/compiler/resolve.js +64 -29
  34. package/lib/compiler/shared.js +16 -4
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +1 -1
  37. package/lib/edm/annotations/edmJson.js +23 -20
  38. package/lib/edm/annotations/genericTranslation.js +12 -10
  39. package/lib/edm/csn2edm.js +50 -56
  40. package/lib/edm/edm.js +33 -28
  41. package/lib/edm/edmInboundChecks.js +2 -2
  42. package/lib/edm/edmPreprocessor.js +54 -88
  43. package/lib/edm/edmUtils.js +9 -12
  44. package/lib/gen/BaseParser.js +63 -52
  45. package/lib/gen/CdlGrammar.checksum +1 -1
  46. package/lib/gen/CdlParser.js +1153 -1165
  47. package/lib/gen/Dictionary.json +21 -1
  48. package/lib/json/from-csn.js +70 -43
  49. package/lib/json/to-csn.js +6 -8
  50. package/lib/language/multiLineStringParser.js +3 -2
  51. package/lib/main.d.ts +58 -24
  52. package/lib/model/cloneCsn.js +3 -0
  53. package/lib/model/csnUtils.js +28 -39
  54. package/lib/model/xprAsTree.js +23 -9
  55. package/lib/modelCompare/compare.js +5 -4
  56. package/lib/optionProcessor.js +24 -17
  57. package/lib/parsers/AstBuildingParser.js +81 -25
  58. package/lib/parsers/XprTree.js +57 -3
  59. package/lib/parsers/identifiers.js +1 -1
  60. package/lib/parsers/index.js +0 -3
  61. package/lib/render/manageConstraints.js +25 -25
  62. package/lib/render/toCdl.js +173 -170
  63. package/lib/render/toHdbcds.js +126 -128
  64. package/lib/render/toRename.js +7 -7
  65. package/lib/render/toSql.js +128 -125
  66. package/lib/render/utils/common.js +47 -22
  67. package/lib/render/utils/delta.js +25 -25
  68. package/lib/render/utils/operators.js +2 -2
  69. package/lib/render/utils/pretty.js +5 -5
  70. package/lib/render/utils/sql.js +13 -13
  71. package/lib/render/utils/standardDatabaseFunctions.js +115 -103
  72. package/lib/render/utils/unique.js +4 -4
  73. package/lib/transform/db/applyTransformations.js +1 -1
  74. package/lib/transform/db/assertUnique.js +2 -2
  75. package/lib/transform/db/associations.js +6 -7
  76. package/lib/transform/db/assocsToQueries/utils.js +4 -5
  77. package/lib/transform/db/backlinks.js +12 -9
  78. package/lib/transform/db/cdsPersistence.js +8 -7
  79. package/lib/transform/db/constraints.js +13 -10
  80. package/lib/transform/db/expansion.js +7 -3
  81. package/lib/transform/db/flattening.js +4 -14
  82. package/lib/transform/db/processSqlServices.js +2 -1
  83. package/lib/transform/db/temporal.js +5 -7
  84. package/lib/transform/db/views.js +2 -4
  85. package/lib/transform/draft/db.js +8 -8
  86. package/lib/transform/draft/odata.js +10 -7
  87. package/lib/transform/forOdata.js +10 -5
  88. package/lib/transform/forRelationalDB.js +5 -75
  89. package/lib/transform/localized.js +1 -1
  90. package/lib/transform/odata/createForeignKeys.js +11 -10
  91. package/lib/transform/odata/flattening.js +8 -4
  92. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
  93. package/lib/transform/odata/typesExposure.js +3 -3
  94. package/lib/transform/transformUtils.js +4 -8
  95. package/lib/transform/translateAssocsToJoins.js +14 -7
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
  97. package/lib/utils/objectUtils.js +0 -17
  98. package/package.json +10 -13
  99. package/share/messages/def-upcoming-virtual-change.md +1 -1
  100. package/LICENSE +0 -37
  101. package/bin/cds_remove_invalid_whitespace.js +0 -138
  102. package/doc/CHANGELOG_ARCHIVE.md +0 -3604
  103. package/lib/gen/genericAntlrParser.js +0 -3
  104. package/lib/gen/language.checksum +0 -1
  105. package/lib/gen/language.interp +0 -456
  106. package/lib/gen/language.tokens +0 -180
  107. package/lib/gen/languageLexer.interp +0 -439
  108. package/lib/gen/languageLexer.js +0 -1483
  109. package/lib/gen/languageLexer.tokens +0 -167
  110. package/lib/gen/languageParser.js +0 -24941
  111. package/lib/language/antlrParser.js +0 -205
  112. package/lib/language/errorStrategy.js +0 -646
  113. package/lib/language/genericAntlrParser.js +0 -1572
  114. package/lib/parsers/CdlGrammar.g4 +0 -2070
@@ -163,14 +163,14 @@ class NameScopeStack {
163
163
  const baseAlias = aliasName;
164
164
  while (this.nonRootSegments.has(aliasName) || this.rootSegments.has(aliasName) || this.#aliasToFqn[aliasName]) {
165
165
  // Alias must be unique among _all_ segments and existing USINGs.
166
- aliasName = `${baseAlias}${++counter}`;
166
+ aliasName = `${ baseAlias }${ ++counter }`;
167
167
  }
168
168
  }
169
169
 
170
170
  // Always add an alias, even if unnecessary, as we'd otherwise try to create
171
171
  // it in #useAliasForPath() again if the same rootName is seen again.
172
172
  if (this.#aliasToFqn[aliasName])
173
- throw new CompilerAssertion(`to.cdl: Alias "${aliasName}" already exists; collision for ${fqn} and ${this.#aliasToFqn[aliasName].path}`);
173
+ throw new CompilerAssertion(`to.cdl: Alias "${ aliasName }" already exists; collision for ${ fqn } and ${ this.#aliasToFqn[aliasName].path }`);
174
174
  const alias = new UsingAlias(fqn, aliasName);
175
175
  this.#aliasToFqn[aliasName] = alias;
176
176
  this.#fqnToAlias[fqn] = alias;
@@ -196,7 +196,7 @@ class NameScopeStack {
196
196
  // - `annotate cds.String;` works
197
197
  return fqn;
198
198
  }
199
- if (fqn.startsWith(`${leaf.name }.`))
199
+ if (fqn.startsWith(`${ leaf.name }.`))
200
200
  return fqn.substring(leaf.name.length + 1); // '+1' => also remove '.'
201
201
  throw new CompilerAssertion('to.cdl: Definition to be rendered is not in current name scope!');
202
202
  }
@@ -222,7 +222,7 @@ class NameScopeStack {
222
222
  for (let i = this.#scopes.length - 1; i >= 1; i--) {
223
223
  const tree = this.#scopes[i];
224
224
 
225
- if (tree.name && fqn.startsWith(`${tree.name }.`)) {
225
+ if (tree.name && fqn.startsWith(`${ tree.name }.`)) {
226
226
  // FQN is in current scope.
227
227
  const relativeName = fqn.substring(tree.name.length + 1);
228
228
  const relativeRoot = rootPathSegment(relativeName);
@@ -363,7 +363,7 @@ function createDefinitionPathTree( csn, options ) {
363
363
  if (!csn.definitions)
364
364
  return tree;
365
365
 
366
- const useNesting = options.renderCdlDefinitionNesting;
366
+ const useNesting = options.renderCdlDefinitionNesting !== false;
367
367
 
368
368
  for (const defName in csn.definitions) {
369
369
  const segments = defName.split('.');
@@ -415,7 +415,7 @@ class CsnToCdl {
415
415
 
416
416
  const env = createEnv();
417
417
 
418
- const useNesting = !!this.options.renderCdlDefinitionNesting;
418
+ const useNesting = this.options.renderCdlDefinitionNesting !== false;
419
419
  this.definitionTree = createDefinitionPathTree(this.csn, this.options);
420
420
  this.commonNamespace = this.getCommonNamespace();
421
421
 
@@ -441,18 +441,13 @@ class CsnToCdl {
441
441
 
442
442
  cdlResult.model = this.renderUsingAliases(env.nameEnvStack.getUsings(), env) + cdlResult.model;
443
443
  if (this.csn.requires) {
444
- let usingsStr = this.csn.requires.map(req => `using from '${req}';`).join('\n');
444
+ let usingsStr = this.csn.requires.map(req => `using from '${ req }';`).join('\n');
445
445
  usingsStr += '\n\n';
446
446
  cdlResult.model = usingsStr + cdlResult.model;
447
447
  }
448
448
 
449
449
  if (this.commonNamespace.name)
450
- cdlResult.model = `namespace ${this.renderArtifactName(this.commonNamespace.name, env)};\n\n${cdlResult.model}`;
451
-
452
- if (this.csn.namespace) {
453
- cdlResult.namespace = `namespace ${this.renderArtifactName(this.csn.namespace, createEnv())};\n`;
454
- cdlResult.namespace += 'using from \'./model.cds\';';
455
- }
450
+ cdlResult.model = `namespace ${ this.renderArtifactName(this.commonNamespace.name, env) };\n\n${ cdlResult.model }`;
456
451
 
457
452
  this.msg.throwWithError();
458
453
  return cdlResult;
@@ -466,7 +461,7 @@ class CsnToCdl {
466
461
  */
467
462
  getCommonNamespace() {
468
463
  let root = this.definitionTree;
469
- if (!this.options.renderCdlDefinitionNesting || !this.options.renderCdlCommonNamespace)
464
+ if (this.options.renderCdlDefinitionNesting === false || this.options.renderCdlCommonNamespace === false)
470
465
  return root; // User does not want common namespace.
471
466
 
472
467
  if (this.csn.vocabularies) {
@@ -506,20 +501,20 @@ class CsnToCdl {
506
501
  * @returns {string}
507
502
  */
508
503
  renderUsingAliases(aliases, env) {
509
- if (!this.options.renderCdlDefinitionNesting) {
504
+ if (this.options.renderCdlDefinitionNesting !== false) {
510
505
  // openAPI importer searches for a single USING statement and replaces it.
511
506
  // Let's try to be backward compatible.
512
- return aliases.length > 0 ? `using { ${aliases.map(entry => (entry.requiresExplicitAlias()
513
- ? `${this.quotePathIfRequired(entry.path, env)} as ${this.quoteNonIdentifierOrKeyword(entry.alias, env)}`
514
- : entry.path)).join(', ')} };\n\n` : '';
507
+ return aliases.length > 0 ? `using { ${ aliases.map(entry => (entry.requiresExplicitAlias()
508
+ ? `${ this.quotePathIfRequired(entry.path, env) } as ${ this.quoteNonIdentifierOrKeyword(entry.alias, env) }`
509
+ : entry.path)).join(', ') } };\n\n` : '';
515
510
  }
516
511
 
517
512
  let result = '';
518
513
  for (const entry of aliases) {
519
514
  if (entry.requiresExplicitAlias())
520
- result += `using { ${this.quotePathIfRequired(entry.path, env)} as ${this.quoteNonIdentifierOrKeyword(entry.alias, env)} };\n`;
515
+ result += `using { ${ this.quotePathIfRequired(entry.path, env) } as ${ this.quoteNonIdentifierOrKeyword(entry.alias, env) } };\n`;
521
516
  else
522
- result += `using { ${entry.path } };\n`;
517
+ result += `using { ${ entry.path } };\n`;
523
518
  }
524
519
  return result !== '' ? `${ result }\n` : result;
525
520
  }
@@ -535,7 +530,7 @@ class CsnToCdl {
535
530
  forEachDefinition(this.csn, (artifact, artifactName) => {
536
531
  const sourceStr = this.renderDefinition(artifactName, artifact, env);
537
532
  if (sourceStr !== '')
538
- result += `${sourceStr}\n`;
533
+ result += `${ sourceStr }\n`;
539
534
  });
540
535
  return result;
541
536
  }
@@ -564,7 +559,7 @@ class CsnToCdl {
564
559
  // Render service/context with nested definitions.
565
560
  env.path = [ 'definitions', entry.name ];
566
561
  result += that.renderAnnotationAssignmentsAndDocComment(def, env);
567
- result += `${env.indent}${def.kind} ${ that.renderArtifactName(entry.name, env) } {\n`;
562
+ result += `${ env.indent }${ def.kind } ${ that.renderArtifactName(entry.name, env) } {\n`;
568
563
  env.increaseIndent();
569
564
  env.nameEnvStack.pushNameEnv(entry);
570
565
  if (entry.children)
@@ -573,12 +568,12 @@ class CsnToCdl {
573
568
  env.decreaseIndent();
574
569
  if (result.at(-1) === '\n' && result.at(-2) === '\n')
575
570
  result = result.substring(0, result.length - 1); // to get the closing brace on the next line after a definition, remove one linebreak
576
- result += `${env.indent}};\n\n`;
571
+ result += `${ env.indent }};\n\n`;
577
572
  }
578
573
  else if (def) {
579
574
  const sourceStr = that.renderDefinition(entry.name, def, env);
580
575
  if (sourceStr !== '')
581
- result += `${sourceStr}\n`;
576
+ result += `${ sourceStr }\n`;
582
577
  if (entry.children)
583
578
  renderTree(entry);
584
579
  }
@@ -617,7 +612,7 @@ class CsnToCdl {
617
612
  // indentation and name resolution issues
618
613
  env.path = [ 'vocabularies', name ];
619
614
  const sourceStr = this.renderArtifact(name, anno, env, 'annotation');
620
- return `${sourceStr}\n`;
615
+ return `${ sourceStr }\n`;
621
616
  }
622
617
 
623
618
  /**
@@ -668,20 +663,20 @@ class CsnToCdl {
668
663
  // Includes can't be combined with anything in braces {}.
669
664
  const affix = isElementExtend ? 'element ' : '';
670
665
  const includes = ext.includes.map((inc, i) => this.renderDefinitionReference(inc, env.withSubPath([ 'includes', i ]))).join(', ');
671
- result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
666
+ result += `${ env.indent }extend ${ affix }${ extName } with ${ includes };\n`;
672
667
  return result;
673
668
  }
674
669
 
675
670
  const typeParams = this.renderTypeParameters(ext, true);
676
671
  if (typeParams) {
677
- result += `${env.indent}extend ${extName} with ${typeParams};\n`;
672
+ result += `${ env.indent }extend ${ extName } with ${ typeParams };\n`;
678
673
  return result;
679
674
  }
680
675
 
681
676
  // If there is nothing to extend, e.g. only annotations, don't render an
682
677
  // empty element list. This would end up in diffs with parseCdl CSN.
683
678
  if (!ext.elements && !ext.columns && !ext.actions && !ext.enum) {
684
- result += `${env.indent}extend ${extName};\n`;
679
+ result += `${ env.indent }extend ${ extName };\n`;
685
680
  return result;
686
681
  }
687
682
 
@@ -691,9 +686,9 @@ class CsnToCdl {
691
686
  // If there are actions, check if there are also elements/columns, and if so, use the prefix notation.
692
687
  const usePrefixNotation = ext.actions && (ext.columns || ext.elements);
693
688
  if (usePrefixNotation)
694
- result += `${env.indent}extend ${this.getExtendPrefixVariant(ext)} ${extName} with {\n`;
689
+ result += `${ env.indent }extend ${ this.getExtendPrefixVariant(ext) } ${ extName } with {\n`;
695
690
  else
696
- result += `${env.indent}extend ${extName} with ${this.getExtendPostfixVariant(ext)}{\n`;
691
+ result += `${ env.indent }extend ${ extName } with ${ this.getExtendPostfixVariant(ext) }{\n`;
697
692
 
698
693
  if (ext.columns)
699
694
  result += this.renderViewColumns(ext, env.withIncreasedIndent());
@@ -711,10 +706,10 @@ class CsnToCdl {
711
706
  if (!usePrefixNotation)
712
707
  result += actions;
713
708
  else if (actions !== '')
714
- result += `${env.indent}} actions {\n${actions}`;
709
+ result += `${ env.indent }} actions {\n${ actions }`;
715
710
  }
716
711
 
717
- result += `${env.indent}};\n`;
712
+ result += `${ env.indent }};\n`;
718
713
  return result;
719
714
  }
720
715
 
@@ -804,14 +799,14 @@ class CsnToCdl {
804
799
  let result = this.renderAnnotationAssignmentsAndDocComment(ext, env);
805
800
  // Note: Not renderDefinitionReference, because we don't care if there
806
801
  // are annotations to unknown things. That's allowed!
807
- result += `${env.indent}annotate ${this.renderArtifactName(ext.annotate, env)}`;
802
+ result += `${ env.indent }annotate ${ this.renderArtifactName(ext.annotate, env) }`;
808
803
 
809
804
  if (ext.params)
810
805
  result += this.renderAnnotateParamsInParentheses(ext, env);
811
806
 
812
807
  // Element extensions and annotations (possibly nested)
813
808
  if (ext.elements || ext.enum)
814
- result += ` ${this.renderAnnotateStatementElements(ext, env)}`;
809
+ result += ` ${ this.renderAnnotateStatementElements(ext, env) }`;
815
810
 
816
811
  else if (ext.returns)
817
812
  result += this.renderAnnotateReturns(ext, env);
@@ -835,7 +830,7 @@ class CsnToCdl {
835
830
  result += ';\n';
836
831
  }
837
832
  env.decreaseIndent();
838
- result += `${env.indent}}`;
833
+ result += `${ env.indent }}`;
839
834
  }
840
835
 
841
836
  result = removeTrailingNewline(result);
@@ -864,12 +859,12 @@ class CsnToCdl {
864
859
  result += env.indent + this.quoteNonIdentifierOrKeyword(name, env);
865
860
  if (elem.elements) {
866
861
  env.path.push('elements');
867
- result += ` ${this.renderAnnotateStatementElements(elem, env)}`;
862
+ result += ` ${ this.renderAnnotateStatementElements(elem, env) }`;
868
863
  env.path.pop();
869
864
  }
870
865
  else if (elem.enum) {
871
866
  env.path.push('enum');
872
- result += ` ${this.renderAnnotateStatementElements(elem, env)}`;
867
+ result += ` ${ this.renderAnnotateStatementElements(elem, env) }`;
873
868
  env.path.pop();
874
869
  }
875
870
 
@@ -877,7 +872,7 @@ class CsnToCdl {
877
872
  }
878
873
  env.path.length -= 2;
879
874
  env.decreaseIndent();
880
- result += `${env.indent}}`;
875
+ result += `${ env.indent }}`;
881
876
  return result;
882
877
  }
883
878
 
@@ -895,11 +890,11 @@ class CsnToCdl {
895
890
 
896
891
  const returnAnnos = this.renderAnnotationAssignmentsAndDocComment(ext.returns, env.withIncreasedIndent());
897
892
  if (returnAnnos)
898
- result += `\n${returnAnnos}`;
893
+ result += `\n${ returnAnnos }`;
899
894
 
900
895
  if (ext.returns.elements) {
901
896
  // Annotations are on separate lines: Have it aligned nicely
902
- result += returnAnnos ? `${env.indent}` : ' ';
897
+ result += returnAnnos ? `${ env.indent }` : ' ';
903
898
  result += this.renderAnnotateStatementElements(ext.returns, env);
904
899
  }
905
900
  return result;
@@ -923,7 +918,7 @@ class CsnToCdl {
923
918
  // const sub = (param.elements || param.enum) ? ` ${renderAnnotateStatementElements(param, childEnv)}` : '';
924
919
  paramAnnotations.push( annos + childEnv.indent + name);
925
920
  });
926
- result += `${paramAnnotations.join(',\n')}\n${env.indent})`;
921
+ result += `${ paramAnnotations.join(',\n') }\n${ env.indent })`;
927
922
  return result;
928
923
  }
929
924
 
@@ -962,7 +957,7 @@ class CsnToCdl {
962
957
  return this.renderArtifact(artifactName, art, env);
963
958
 
964
959
  default:
965
- throw new ModelError(`to.cdl: Unknown artifact kind: '${art.kind}' at ${JSON.stringify(env.path)}`);
960
+ throw new ModelError(`to.cdl: Unknown artifact kind: '${ art.kind }' at ${ JSON.stringify(env.path) }`);
966
961
  }
967
962
  }
968
963
 
@@ -981,7 +976,7 @@ class CsnToCdl {
981
976
  const normalizedArtifactName = kind !== 'annotation'
982
977
  ? this.renderArtifactName(artifactName, env)
983
978
  : this.quotePathIfRequired(artifactName, env);
984
- result += `${env.indent}${kind} ${normalizedArtifactName}`;
979
+ result += `${ env.indent }${ kind } ${ normalizedArtifactName }`;
985
980
 
986
981
  if (art.params)
987
982
  result += this.renderParameters(art, env);
@@ -1006,7 +1001,7 @@ class CsnToCdl {
1006
1001
  }
1007
1002
 
1008
1003
  // For nicer output, no colon if unnamed structure is used.
1009
- result += (!art.type && art.elements) ? ` ${type}` : ` : ${type}`;
1004
+ result += (!art.type && art.elements) ? ` ${ type }` : ` : ${ type }`;
1010
1005
  }
1011
1006
  else {
1012
1007
  this.msg.warning('syntax-missing-type', env.path, { '#': art.kind, name: artifactName }, {
@@ -1043,8 +1038,8 @@ class CsnToCdl {
1043
1038
  */
1044
1039
  renderContextOrService( artifactName, art, env ) {
1045
1040
  let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
1046
- result += `${env.indent}${art.kind} ${this.renderArtifactName(artifactName, env)}`;
1047
- return `${result} {};\n`;
1041
+ result += `${ env.indent }${ art.kind } ${ this.renderArtifactName(artifactName, env) }`;
1042
+ return `${ result } {};\n`;
1048
1043
  }
1049
1044
 
1050
1045
  /**
@@ -1059,17 +1054,17 @@ class CsnToCdl {
1059
1054
  */
1060
1055
  renderAspect( artifactName, art, env ) {
1061
1056
  let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
1062
- result += `${env.indent}aspect ${this.renderArtifactName(artifactName, env)}`;
1057
+ result += `${ env.indent }aspect ${ this.renderArtifactName(artifactName, env) }`;
1063
1058
  if (art.includes)
1064
1059
  result += this.renderIncludes(art.includes, env);
1065
1060
 
1066
1061
  if (art.elements)
1067
- result += ` ${this.renderElements(art, env)}`;
1062
+ result += ` ${ this.renderElements(art, env) }`;
1068
1063
  else if (art.actions)
1069
1064
  // if there are no elements, but actions, CDL syntax requires braces.
1070
1065
  result += ' { }';
1071
1066
 
1072
- result += `${this.renderBoundActionsAndFunctions(art, env)};\n`;
1067
+ result += `${ this.renderBoundActionsAndFunctions(art, env) };\n`;
1073
1068
  return result;
1074
1069
  }
1075
1070
 
@@ -1086,7 +1081,7 @@ class CsnToCdl {
1086
1081
  for (const name in artifact.elements)
1087
1082
  elements += this.renderElement(name, artifact.elements[name], childEnv.withSubPath([ 'elements', name ]));
1088
1083
 
1089
- return (elements === '') ? '{ }' : `{\n${elements}${env.indent}}`;
1084
+ return (elements === '') ? '{ }' : `{\n${ elements }${ env.indent }}`;
1090
1085
  }
1091
1086
 
1092
1087
  /**
@@ -1107,10 +1102,10 @@ class CsnToCdl {
1107
1102
  result += element.masked ? 'masked ' : '';
1108
1103
  result += this.quoteNonIdentifierOrKeyword(elementName, env);
1109
1104
  if (element['#'] !== undefined) { // enum symbol reference
1110
- result += ` = #${element['#']}`;
1105
+ result += ` = #${ element['#'] }`;
1111
1106
  }
1112
1107
  else if (element.val !== undefined) { // enum value
1113
- result += ` = ${this.exprRenderer.renderExpr(element, env)}`;
1108
+ result += ` = ${ this.exprRenderer.renderExpr(element, env) }`;
1114
1109
  }
1115
1110
  else if (!isCalcElement || !isDirectAssocOrComp(element.type) && !element.$filtered && !element.$enclosed) {
1116
1111
  // If the element is a calculated element _and_ a direct association or
@@ -1120,7 +1115,7 @@ class CsnToCdl {
1120
1115
  // we'd get a cast to an association.
1121
1116
  const props = this.renderTypeReferenceAndProps(element, env);
1122
1117
  if (props !== '')
1123
- result += ` : ${props}`;
1118
+ result += ` : ${ props }`;
1124
1119
  }
1125
1120
 
1126
1121
  if (isCalcElement) { // calculated element // @ts-ignore
@@ -1135,7 +1130,7 @@ class CsnToCdl {
1135
1130
  env.path.length -= 1;
1136
1131
  }
1137
1132
 
1138
- return `${result};\n`;
1133
+ return `${ result };\n`;
1139
1134
  }
1140
1135
 
1141
1136
  /**
@@ -1258,7 +1253,7 @@ class CsnToCdl {
1258
1253
  // Sub-SELECT
1259
1254
  if (source.SELECT || source.SET) {
1260
1255
  const subEnv = env.withIncreasedIndent();
1261
- let result = `(\n${subEnv.indent}${this.renderQuery(source, false, 'view', subEnv)}\n${env.indent})`;
1256
+ let result = `(\n${ subEnv.indent }${ this.renderQuery(source, false, 'view', subEnv) }\n${ env.indent })`;
1262
1257
  if (source.as)
1263
1258
  result += this.renderAlias(source.as, env);
1264
1259
 
@@ -1268,17 +1263,17 @@ class CsnToCdl {
1268
1263
  else if (source.join) {
1269
1264
  // One join operation, possibly with ON-condition
1270
1265
  env.path.push('args', 0);
1271
- let result = `(${this.renderViewSource(source.args[0], env)}`;
1266
+ let result = `(${ this.renderViewSource(source.args[0], env) }`;
1272
1267
  for (let i = 1; i < source.args.length; i++) {
1273
1268
  env.path[env.path.length - 1] = i;
1274
- result += ` ${source.join} `;
1269
+ result += ` ${ source.join } `;
1275
1270
  result += this.renderJoinCardinality(source.cardinality);
1276
- result += `join ${this.renderViewSource(source.args[i], env)}`;
1271
+ result += `join ${ this.renderViewSource(source.args[i], env) }`;
1277
1272
  }
1278
1273
  env.path.length -= 2;
1279
1274
  if (source.on) {
1280
1275
  env.path.push('on');
1281
- result += ` on ${this.exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ]))}`;
1276
+ result += ` on ${ this.exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ])) }`;
1282
1277
  env.path.length -= 1;
1283
1278
  }
1284
1279
  result += ')';
@@ -1316,7 +1311,7 @@ class CsnToCdl {
1316
1311
  renderAbsolutePath( path, env ) {
1317
1312
  // Sanity checks
1318
1313
  if (!path.ref)
1319
- throw new ModelError(`Expecting ref in path: ${JSON.stringify(path)}`);
1314
+ throw new ModelError(`Expecting ref in path: ${ JSON.stringify(path) }`);
1320
1315
 
1321
1316
  // Determine the absolute name of the first artifact on the path (before any associations or element traversals)
1322
1317
  const firstArtifactName = path.ref[0].id || path.ref[0];
@@ -1327,14 +1322,14 @@ class CsnToCdl {
1327
1322
  // Even the first step might have parameters and/or a filter
1328
1323
  env.path.push('ref', 0);
1329
1324
  if (path.ref[0].args)
1330
- result += `(${this.renderArguments(path.ref[0], ':', env)})`;
1325
+ result += `(${ this.renderArguments(path.ref[0], ':', env) })`;
1331
1326
  if (path.ref[0].where)
1332
1327
  result += this.renderFilterAndCardinality(path.ref[0], env);
1333
1328
  env.path.length -= 2;
1334
1329
 
1335
1330
  // Add any path steps (possibly with parameters and filters) that may follow after that
1336
1331
  if (path.ref.length > 1)
1337
- result += `:${this.exprRenderer.renderExpr({ ref: path.ref.slice(1) }, env)}`;
1332
+ result += `:${ this.exprRenderer.renderExpr({ ref: path.ref.slice(1) }, env) }`;
1338
1333
 
1339
1334
  return result;
1340
1335
  }
@@ -1387,7 +1382,7 @@ class CsnToCdl {
1387
1382
  return this.renderViewColumn(col, env, findElement(elements, col));
1388
1383
  }).join(',\n');
1389
1384
  env.path.length -= 2;
1390
- return `${result}\n`;
1385
+ return `${ result }\n`;
1391
1386
  }
1392
1387
 
1393
1388
  /**
@@ -1414,28 +1409,37 @@ class CsnToCdl {
1414
1409
  result += col.virtual ? 'virtual ' : '';
1415
1410
  result += col.key ? 'key ' : '';
1416
1411
 
1412
+ let exprResult;
1417
1413
  // Use special rendering for .expand/.inline - renderExpr cannot easily handle some cases
1418
1414
  if (col.expand || col.inline)
1419
- result += this.renderInlineExpand(col, env);
1415
+ exprResult = this.renderInlineExpand(col, env);
1420
1416
  else if (col.xpr && xprContainsCondition(col.xpr))
1421
- result += this.exprRenderer.renderSubExpr(withoutCast(col), env);
1417
+ exprResult = this.exprRenderer.renderSubExpr(withoutCast(col), env);
1422
1418
  else
1423
- result += this.exprRenderer.renderExpr(withoutCast(col), env);
1419
+ exprResult = this.exprRenderer.renderExpr(withoutCast(col), env);
1420
+
1421
+ result += exprResult;
1424
1422
 
1425
- // Alias for inline/expand is already handled by renderInlineExpand
1426
1423
  // A new association (cast with `type` and `target`) uses `as` as its primary name, not alias.
1427
- const isNewAssociation = col.cast?.type && col.cast.target;
1428
- if (!isNewAssociation && col.as && !col.inline && !col.expand)
1424
+ // The same for `virtual <name>`, which is a new element definition instead of a reference.
1425
+ const isNewElementDefinition = exprResult === '';
1426
+ if (isNewElementDefinition && col.as) {
1427
+ result += this.quoteNonIdentifierOrKeyword(col.as, env);
1428
+ }
1429
+ else if (col.as && !col.inline && !col.expand) {
1430
+ // Alias for inline/expand is already handled by renderInlineExpand
1429
1431
  result += this.renderAlias(col.as, env);
1432
+ }
1433
+
1430
1434
 
1431
1435
  // Explicit type provided for the view element?
1432
1436
  if (col.cast) {
1433
1437
  env.path.push('cast');
1434
1438
  // Special case: Explicit association type is actually a redirect
1435
1439
  if (col.cast.target && !col.cast.type)
1436
- result += ` : ${this.renderRedirectedTo(col.cast, env)}`;
1440
+ result += ` : ${ this.renderRedirectedTo(col.cast, env) }`;
1437
1441
  else
1438
- result += ` : ${this.renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
1442
+ result += ` : ${ this.renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true }) }`;
1439
1443
  env.path.length -= 1;
1440
1444
  }
1441
1445
  return result;
@@ -1463,9 +1467,9 @@ class CsnToCdl {
1463
1467
  if (!obj.inline && !obj.expand) {
1464
1468
  env.path.push('cast');
1465
1469
  if (obj.cast && obj.cast.type)
1466
- result += ` : ${this.renderTypeReferenceAndProps(obj.cast, env, { noAnnoCollect: true })}`;
1470
+ result += ` : ${ this.renderTypeReferenceAndProps(obj.cast, env, { noAnnoCollect: true }) }`;
1467
1471
  else if (obj.cast && obj.cast.target) // test tbd
1468
- result += ` : ${this.renderRedirectedTo(obj.cast, env)}`;
1472
+ result += ` : ${ this.renderRedirectedTo(obj.cast, env) }`;
1469
1473
  env.path.length -= 1;
1470
1474
  return result;
1471
1475
  }
@@ -1481,11 +1485,11 @@ class CsnToCdl {
1481
1485
  result += expandInline //
1482
1486
  .map(elm => this.renderAnnotationAssignmentsAndDocComment(elm, childEnv) + childEnv.indent + this.renderInlineExpand(elm, childEnv))
1483
1487
  .join(',\n');
1484
- result += `\n${env.indent}}`;
1488
+ result += `\n${ env.indent }}`;
1485
1489
 
1486
1490
  // Don't forget about the .excluding
1487
1491
  if (obj.excluding)
1488
- result += ` excluding { ${obj.excluding.join(',')} }`;
1492
+ result += ` excluding { ${ obj.excluding.join(',') } }`;
1489
1493
 
1490
1494
  // { * } as expand
1491
1495
  if (obj.as && isAnonymousExpand)
@@ -1505,7 +1509,7 @@ class CsnToCdl {
1505
1509
  if (!obj || obj && obj.doc === undefined)
1506
1510
  return '';
1507
1511
  else if (obj && obj.doc === null) // empty doc comment needs to be rendered
1508
- return `\n${env.indent}/** */\n`;
1512
+ return `\n${ env.indent }/** */\n`;
1509
1513
 
1510
1514
  let { doc } = obj;
1511
1515
  if (/[*]\//.test(doc)) // only escape sequence allowed in CDL for doc comments
@@ -1514,10 +1518,10 @@ class CsnToCdl {
1514
1518
  // Smaller comment for single-line comments. If the comment starts or ends with whitespace
1515
1519
  // we must use a block comment, or it will be lost when compiling the source again.
1516
1520
  if (!obj.doc.includes('\n') && !/(^\s)|(\s$)/.test(obj.doc))
1517
- return `${env.indent}/** ${doc} */\n`;
1521
+ return `${ env.indent }/** ${ doc } */\n`;
1518
1522
 
1519
- const comment = doc.split('\n').map(l => `${env.indent} * ${l}`).join('\n');
1520
- return `${env.indent}/**\n${comment}\n${env.indent} */\n`;
1523
+ const comment = doc.split('\n').map(l => `${ env.indent } * ${ l }`).join('\n');
1524
+ return `${ env.indent }/**\n${ comment }\n${ env.indent } */\n`;
1521
1525
  }
1522
1526
 
1523
1527
  /**
@@ -1528,7 +1532,7 @@ class CsnToCdl {
1528
1532
  renderView( artifactName, art, env ) {
1529
1533
  const syntax = (art.projection) ? 'projection' : 'entity';
1530
1534
  let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
1531
- result += `${env.indent}entity ${this.renderArtifactName(artifactName, env)}`;
1535
+ result += `${ env.indent }entity ${ this.renderArtifactName(artifactName, env) }`;
1532
1536
  if (art.params)
1533
1537
  result += this.renderParameters(art, env);
1534
1538
  result += ' as ';
@@ -1562,7 +1566,7 @@ class CsnToCdl {
1562
1566
  }
1563
1567
  else if (!query.SELECT) {
1564
1568
  // ...otherwise must have a SELECT
1565
- throw new ModelError(`Unexpected query operation ${JSON.stringify(query)}`);
1569
+ throw new ModelError(`Unexpected query operation ${ JSON.stringify(query) }`);
1566
1570
  }
1567
1571
 
1568
1572
  let result = '';
@@ -1588,7 +1592,7 @@ class CsnToCdl {
1588
1592
  if (elems) {
1589
1593
  result += ' mixin {\n';
1590
1594
  result += elems;
1591
- result += `${env.indent}} into`;
1595
+ result += `${ env.indent }} into`;
1592
1596
  }
1593
1597
  }
1594
1598
  result += select.distinct ? ' distinct' : '';
@@ -1597,33 +1601,33 @@ class CsnToCdl {
1597
1601
  env.increaseIndent();
1598
1602
  result += this.renderViewColumns(select, env, elements);
1599
1603
  env.decreaseIndent();
1600
- result += `${env.indent}}`;
1604
+ result += `${ env.indent }}`;
1601
1605
  }
1602
1606
 
1603
1607
  const childEnv = env.withIncreasedIndent();
1604
1608
  if (select.excluding) {
1605
- const excludes = select.excluding.map(id => `${childEnv.indent}${this.quoteNonIdentifierOrKeyword(id, env)}`).join(',\n');
1606
- result += ` excluding {\n${excludes}\n`;
1607
- result += `${env.indent}}`;
1609
+ const excludes = select.excluding.map(id => `${ childEnv.indent }${ this.quoteNonIdentifierOrKeyword(id, env) }`).join(',\n');
1610
+ result += ` excluding {\n${ excludes }\n`;
1611
+ result += `${ env.indent }}`;
1608
1612
  }
1609
1613
 
1610
1614
  if (isLeadingQuery && query.actions)
1611
1615
  result += this.renderBoundActionsAndFunctions(query, env);
1612
1616
 
1613
1617
  if (select.where)
1614
- result += `${continueIndent(result, env)}where ${this.exprRenderer.renderExpr(select.where, env.withSubPath([ 'where' ]))}`;
1618
+ result += `${ continueIndent(result, env) }where ${ this.exprRenderer.renderExpr(select.where, env.withSubPath([ 'where' ])) }`;
1615
1619
 
1616
1620
  if (select.groupBy)
1617
- result += `${continueIndent(result, env)}group by ${select.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
1621
+ result += `${ continueIndent(result, env) }group by ${ select.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ') }`;
1618
1622
 
1619
1623
  if (select.having)
1620
- result += `${continueIndent(result, env)}having ${this.exprRenderer.renderExpr(select.having, env.withSubPath([ 'having' ]))}`;
1624
+ result += `${ continueIndent(result, env) }having ${ this.exprRenderer.renderExpr(select.having, env.withSubPath([ 'having' ])) }`;
1621
1625
 
1622
1626
  if (select.orderBy)
1623
- result += `${continueIndent(result, env)}order by ${select.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
1627
+ result += `${ continueIndent(result, env) }order by ${ select.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ') }`;
1624
1628
 
1625
1629
  if (select.limit)
1626
- result += `${continueIndent(result, env)}${this.renderLimit(select.limit, env.withSubPath([ 'limit' ]))}`;
1630
+ result += `${ continueIndent(result, env) }${ this.renderLimit(select.limit, env.withSubPath([ 'limit' ])) }`;
1627
1631
 
1628
1632
  return result;
1629
1633
 
@@ -1640,7 +1644,7 @@ class CsnToCdl {
1640
1644
  return ' ';
1641
1645
  }
1642
1646
  // Otherwise, start new line and indent normally
1643
- return `\n${indentEnv.withIncreasedIndent().indent}`;
1647
+ return `\n${ indentEnv.withIncreasedIndent().indent }`;
1644
1648
  }
1645
1649
 
1646
1650
  /**
@@ -1653,17 +1657,17 @@ class CsnToCdl {
1653
1657
  // First arg may be leading query
1654
1658
  const subEnv = env.withSubPath([ 'SET', 'args', i ]);
1655
1659
  const subQuery = that.renderQuery(arg, isLeadingQuery && (i === 0), 'view', subEnv, elements);
1656
- return `(${subQuery})`;
1660
+ return `(${ subQuery })`;
1657
1661
  });
1658
1662
 
1659
- let setResult = subQueries.join(`\n${env.indent}${query.SET.op}${query.SET.all ? ' all' : ''} `);
1663
+ let setResult = subQueries.join(`\n${ env.indent }${ query.SET.op }${ query.SET.all ? ' all' : '' } `);
1660
1664
  // Set operation may also have an ORDER BY and LIMIT/OFFSET (in contrast to the ones belonging to
1661
1665
  // each SELECT)
1662
1666
  if (query.SET.orderBy)
1663
- setResult += `${continueIndent(setResult, env)}order by ${query.SET.orderBy.map((entry, i) => that.renderOrderByEntry(entry, env.withSubPath([ 'SET', 'orderBy', i ]))).join(', ')}`;
1667
+ setResult += `${ continueIndent(setResult, env) }order by ${ query.SET.orderBy.map((entry, i) => that.renderOrderByEntry(entry, env.withSubPath([ 'SET', 'orderBy', i ]))).join(', ') }`;
1664
1668
 
1665
1669
  if (query.SET.limit)
1666
- setResult += `${continueIndent(setResult, env)}${that.renderLimit(query.SET.limit, env.withSubPath([ 'SET', 'limit' ]))}`;
1670
+ setResult += `${ continueIndent(setResult, env) }${ that.renderLimit(query.SET.limit, env.withSubPath([ 'SET', 'limit' ])) }`;
1667
1671
  return setResult;
1668
1672
  }
1669
1673
  }
@@ -1679,10 +1683,10 @@ class CsnToCdl {
1679
1683
  renderOrderByEntry( entry, env ) {
1680
1684
  let result = this.renderAnnotationAssignmentsAndDocComment(entry, env) + this.exprRenderer.renderExpr(entry, env);
1681
1685
  if (entry.sort)
1682
- result += ` ${entry.sort}`;
1686
+ result += ` ${ entry.sort }`;
1683
1687
 
1684
1688
  if (entry.nulls)
1685
- result += ` nulls ${entry.nulls}`;
1689
+ result += ` nulls ${ entry.nulls }`;
1686
1690
 
1687
1691
  return result;
1688
1692
  }
@@ -1697,11 +1701,11 @@ class CsnToCdl {
1697
1701
  renderLimit( limit, limitEnv ) {
1698
1702
  let limitStr = '';
1699
1703
  if (limit.rows !== undefined)
1700
- limitStr += `limit ${this.exprRenderer.renderExpr(limit.rows, limitEnv.withSubPath([ 'rows' ]))}`;
1704
+ limitStr += `limit ${ this.exprRenderer.renderExpr(limit.rows, limitEnv.withSubPath([ 'rows' ])) }`;
1701
1705
 
1702
1706
  if (limit.offset !== undefined) {
1703
- const offsetIndent = (limitStr === '') ? '' : `\n${limitEnv.withIncreasedIndent().indent}`;
1704
- limitStr += `${offsetIndent}offset ${this.exprRenderer.renderExpr(limit.offset, limitEnv.withSubPath([ 'offset' ]))}`;
1707
+ const offsetIndent = (limitStr === '') ? '' : `\n${ limitEnv.withIncreasedIndent().indent }`;
1708
+ limitStr += `${ offsetIndent }offset ${ this.exprRenderer.renderExpr(limit.offset, limitEnv.withSubPath([ 'offset' ])) }`;
1705
1709
  }
1706
1710
  return limitStr;
1707
1711
  }
@@ -1724,7 +1728,7 @@ class CsnToCdl {
1724
1728
  result += this.renderActionOrFunction(name, art.actions[name], childEnv.withSubPath([ 'actions', name ]), true);
1725
1729
  result = (result === '')
1726
1730
  ? ' actions { }'
1727
- : ` actions {\n${result}${env.indent}}`;
1731
+ : ` actions {\n${ result }${ env.indent }}`;
1728
1732
  }
1729
1733
  return result;
1730
1734
  }
@@ -1742,10 +1746,10 @@ class CsnToCdl {
1742
1746
  let result = this.renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
1743
1747
  if (isBound) {
1744
1748
  // for bound actions, paths are not global
1745
- result += ` ${this.quotePathIfRequired(actionName, env)}`;
1749
+ result += ` ${ this.quotePathIfRequired(actionName, env) }`;
1746
1750
  }
1747
1751
  else {
1748
- result += ` ${this.renderArtifactName(actionName, env)}`;
1752
+ result += ` ${ this.renderArtifactName(actionName, env) }`;
1749
1753
  }
1750
1754
  result += this.renderParameters(act, env);
1751
1755
  if (act.returns) {
@@ -1754,7 +1758,7 @@ class CsnToCdl {
1754
1758
  if (annos) // if `returns` has annotations, increase indent for nicer aligned output
1755
1759
  actEnv = actEnv.withIncreasedIndent();
1756
1760
  const type = this.renderTypeReferenceAndProps(act.returns, actEnv);
1757
- result += ` returns${annos ? '\n' : ' '}${annos}${annos ? actEnv.indent : ''}${type}`;
1761
+ result += ` returns${ annos ? '\n' : ' ' }${ annos }${ annos ? actEnv.indent : '' }${ type }`;
1758
1762
  }
1759
1763
 
1760
1764
  result += ';\n';
@@ -1775,7 +1779,7 @@ class CsnToCdl {
1775
1779
  const parameters = Object.keys(art.params || {}).map(name => this.renderParameter(name, art.params[name], childEnv));
1776
1780
  if (parameters.length === 0)
1777
1781
  return '()';
1778
- return `(\n${parameters.join(',\n')}\n${env.indent})`;
1782
+ return `(\n${ parameters.join(',\n') }\n${ env.indent })`;
1779
1783
  }
1780
1784
 
1781
1785
  /**
@@ -1788,8 +1792,8 @@ class CsnToCdl {
1788
1792
  */
1789
1793
  renderParameter( parName, par, env ) {
1790
1794
  env = env.withSubPath( [ 'params', parName ]);
1791
- let result = `${this.renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
1792
- result += `${this.quoteNonIdentifierOrKeyword(parName, env)} : ${this.renderTypeReferenceAndProps(par, env)}`;
1795
+ let result = `${ this.renderAnnotationAssignmentsAndDocComment(par, env) }${ env.indent }`;
1796
+ result += `${ this.quoteNonIdentifierOrKeyword(parName, env) } : ${ this.renderTypeReferenceAndProps(par, env) }`;
1793
1797
  return result;
1794
1798
  }
1795
1799
 
@@ -1810,7 +1814,7 @@ class CsnToCdl {
1810
1814
  const { typeRefOnly, noAnnoCollect } = config;
1811
1815
 
1812
1816
  if (typeRefOnly && !artifact.type)
1813
- throw new ModelError(`Expected artifact to have a type; in: ${JSON.stringify(env.path)}`);
1817
+ throw new ModelError(`Expected artifact to have a type; in: ${ JSON.stringify(env.path) }`);
1814
1818
 
1815
1819
  if (artifact.localized) // works even for type definitions
1816
1820
  result += 'localized ';
@@ -1863,7 +1867,7 @@ class CsnToCdl {
1863
1867
 
1864
1868
  // ON-condition (if any)
1865
1869
  if (artifact.on)
1866
- result += ` on ${this.exprRenderer.renderExpr(artifact.on, env.withSubPath([ 'on' ]))}`;
1870
+ result += ` on ${ this.exprRenderer.renderExpr(artifact.on, env.withSubPath([ 'on' ])) }`;
1867
1871
 
1868
1872
  // Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
1869
1873
  if (artifact.keys && !artifact.on)
@@ -1918,9 +1922,9 @@ class CsnToCdl {
1918
1922
  * @return {string}
1919
1923
  */
1920
1924
  renderRedirectedTo( art, env ) {
1921
- let result = `redirected to ${this.renderDefinitionReference(art.target, env)}`;
1925
+ let result = `redirected to ${ this.renderDefinitionReference(art.target, env) }`;
1922
1926
  if (art.on)
1923
- result += ` on ${this.exprRenderer.renderExpr(art.on, env.withSubPath([ 'on' ]))}`;
1927
+ result += ` on ${ this.exprRenderer.renderExpr(art.on, env.withSubPath([ 'on' ])) }`;
1924
1928
  else if (art.keys)
1925
1929
  result += ` ${ this.renderForeignKeys(art, env) }`;
1926
1930
  return result;
@@ -1953,7 +1957,7 @@ class CsnToCdl {
1953
1957
  const childEnv = env.withIncreasedIndent();
1954
1958
  for (const name in enumPart)
1955
1959
  result += this.renderElement(name, enumPart[name], childEnv.withSubPath([ 'enum', name ]));
1956
- result += `${env.indent}}`;
1960
+ result += `${ env.indent }}`;
1957
1961
  return result;
1958
1962
  }
1959
1963
 
@@ -1969,7 +1973,7 @@ class CsnToCdl {
1969
1973
  if (isAnnotationExpression(annoValue)) {
1970
1974
  // Once inside an expression, we stay there.
1971
1975
  const xpr = this.exprRenderer.renderExpr(annoValue, env);
1972
- return `( ${xpr} )`;
1976
+ return `( ${ xpr } )`;
1973
1977
  }
1974
1978
  else if (Array.isArray(annoValue)) {
1975
1979
  return this.renderAnnotationArrayValue( annoValue, env );
@@ -1977,7 +1981,7 @@ class CsnToCdl {
1977
1981
  else if (typeof annoValue === 'object' && annoValue !== null) {
1978
1982
  // Enum symbol
1979
1983
  if (annoValue['#'] !== undefined) {
1980
- return `#${annoValue['#']}`;
1984
+ return `#${ annoValue['#'] }`;
1981
1985
  }
1982
1986
  // Shorthand for absolute path (as string)
1983
1987
  else if (annoValue['='] !== undefined) {
@@ -1989,7 +1993,7 @@ class CsnToCdl {
1989
1993
  else if (annoValue['...'] !== undefined) {
1990
1994
  if (annoValue['...'] === true)
1991
1995
  return '...';
1992
- return `... up to ${this.renderAnnotationValue(annoValue['...'], env)}`;
1996
+ return `... up to ${ this.renderAnnotationValue(annoValue['...'], env) }`;
1993
1997
  }
1994
1998
 
1995
1999
  // Struct value (can currently only occur within an array)
@@ -1997,7 +2001,7 @@ class CsnToCdl {
1997
2001
  // struct if there are more and use nicer indentation.
1998
2002
  const keys = Object.keys(annoValue);
1999
2003
  const childEnv = env.withIncreasedIndent();
2000
- const values = keys.map(key => `${this.quoteAnnotationPathIfRequired(key, env)}: ${this.renderAnnotationValue(annoValue[key], childEnv.withSubPath([ key ]))}`);
2004
+ const values = keys.map(key => `${ this.quoteAnnotationPathIfRequired(key, env) }: ${ this.renderAnnotationValue(annoValue[key], childEnv.withSubPath([ key ])) }`);
2001
2005
  const result = joinDocuments(values, [ ',', line() ]);
2002
2006
  return format(nestBy(env.indent.length, bracketBlock(INDENT_SIZE, '{', result, '}') ));
2003
2007
  }
@@ -2050,17 +2054,17 @@ class CsnToCdl {
2050
2054
  else if (typeof s === 'object') {
2051
2055
  // Sanity check
2052
2056
  if (!s.func && !s.id)
2053
- throw new ModelError(`Unknown path step object: ${JSON.stringify(s)}`);
2057
+ throw new ModelError(`Unknown path step object: ${ JSON.stringify(s) }`);
2054
2058
 
2055
2059
  // Not really a path step but an object-like function call
2056
2060
  if (s.func)
2057
- return `${s.func}(${this.renderArguments(s, '=>', env)})`;
2061
+ return `${ s.func }(${ this.renderArguments(s, '=>', env) })`;
2058
2062
 
2059
2063
  // Path step, possibly with view parameters and/or filters
2060
- let result = `${this.quoteNonIdentifierOrKeyword(s.id, env)}`;
2064
+ let result = `${ this.quoteNonIdentifierOrKeyword(s.id, env) }`;
2061
2065
  if (s.args) {
2062
2066
  // View parameters
2063
- result += `(${this.renderArguments(s, ':', env)})`;
2067
+ result += `(${ this.renderArguments(s, ':', env) })`;
2064
2068
  }
2065
2069
 
2066
2070
  result += this.renderFilterAndCardinality(s, env);
@@ -2068,7 +2072,7 @@ class CsnToCdl {
2068
2072
  return result;
2069
2073
  }
2070
2074
 
2071
- throw new ModelError(`Unknown path step: ${JSON.stringify(s)}`);
2075
+ throw new ModelError(`Unknown path step: ${ JSON.stringify(s) }`);
2072
2076
  }
2073
2077
 
2074
2078
  /**
@@ -2087,7 +2091,7 @@ class CsnToCdl {
2087
2091
  return this.renderPositionalArguments(node, env);
2088
2092
  else if (typeof node.args === 'object')
2089
2093
  return this.renderNamedArguments(node, sep, env);
2090
- throw new ModelError(`Unknown args: ${JSON.stringify(node.args)}; expected array/object`);
2094
+ throw new ModelError(`Unknown args: ${ JSON.stringify(node.args) }; expected array/object`);
2091
2095
  }
2092
2096
 
2093
2097
  /**
@@ -2102,7 +2106,7 @@ class CsnToCdl {
2102
2106
  renderNamedArguments( node, separator, env ) {
2103
2107
  const that = this;
2104
2108
  return Object.keys(node.args).map(function renderNamedArgument(key) {
2105
- return `${that.quoteNonIdentifierOrKeyword(key, env)} ${separator} ${that.renderArgument(node.args[key], env.withSubPath([ 'args', key ]))}`;
2109
+ return `${ that.quoteNonIdentifierOrKeyword(key, env) } ${ separator } ${ that.renderArgument(node.args[key], env.withSubPath([ 'args', key ])) }`;
2106
2110
  }).join(', ');
2107
2111
  }
2108
2112
 
@@ -2172,13 +2176,13 @@ class CsnToCdl {
2172
2176
 
2173
2177
  let result = '[';
2174
2178
  if (card.src !== undefined)
2175
- result += `${card.src}, `;
2179
+ result += `${ card.src }, `;
2176
2180
  if (card.min !== undefined)
2177
- result += `${card.min}..`;
2181
+ result += `${ card.min }..`;
2178
2182
  if (card.max !== undefined)
2179
2183
  result += card.max;
2180
2184
  // srcmin can't be represented in CDL
2181
- return `${result}]${suffix}`;
2185
+ return `${ result }]${ suffix }`;
2182
2186
  }
2183
2187
 
2184
2188
  /**
@@ -2217,34 +2221,34 @@ class CsnToCdl {
2217
2221
 
2218
2222
  renderFilterAndCardinality( s, env ) {
2219
2223
  let result = '';
2220
- const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
2224
+ const cardinality = s.cardinality ? (`${ s.cardinality.max }: `) : '';
2221
2225
  let filter = '';
2222
2226
 
2223
2227
  // TODO: Unify with other filter rendering for SELECT
2224
2228
  if (s.groupBy)
2225
- filter += ` group by ${s.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
2229
+ filter += ` group by ${ s.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ') }`;
2226
2230
  if (s.having)
2227
- filter += ` having ${this.exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
2231
+ filter += ` having ${ this.exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ])) }`;
2228
2232
  if (s.orderBy)
2229
- filter += ` order by ${s.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
2233
+ filter += ` order by ${ s.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ') }`;
2230
2234
  if (s.limit)
2231
- filter += ` ${this.renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
2235
+ filter += ` ${ this.renderLimit(s.limit, env.withSubPath([ 'limit' ])) }`;
2232
2236
 
2233
2237
  if (s.where) {
2234
2238
  let where = this.exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
2235
2239
  // Special rules in CDS parser: If filter starts with one of these SQL keywords, WHERE is mandatory.
2236
2240
  if (filter || /^(?:group|having|order|limit)\s/i.test(where))
2237
- where = ` where ${where}`;
2238
- filter = `${where} ${filter}`;
2241
+ where = ` where ${ where }`;
2242
+ filter = `${ where } ${ filter }`;
2239
2243
  }
2240
2244
 
2241
2245
  filter = filter.trim();
2242
2246
 
2243
2247
  if (cardinality || filter) {
2244
2248
  if (filter.endsWith(']')) // for cases such as [… ![id] ]
2245
- result += `[ ${cardinality}${filter} ]`;
2249
+ result += `[ ${ cardinality }${ filter } ]`;
2246
2250
  else
2247
- result += `[${cardinality}${filter}]`;
2251
+ result += `[${ cardinality }${ filter }]`;
2248
2252
  }
2249
2253
  return result;
2250
2254
  }
@@ -2294,13 +2298,13 @@ class CsnToCdl {
2294
2298
 
2295
2299
  const alias = fKey.as ? this.renderAlias(fKey.as, env) : '';
2296
2300
  const key = this.exprRenderer.renderExpr(fKey, env);
2297
- renderedKeys.push(`${key}${alias},`);
2301
+ renderedKeys.push(`${ key }${ alias },`);
2298
2302
  }
2299
2303
 
2300
2304
  if (hasAnnotations) {
2301
- const sep = `\n${env.indent}`;
2305
+ const sep = `\n${ env.indent }`;
2302
2306
  env.decreaseIndent();
2303
- return `{${sep}${renderedKeys.join(sep)}\n${env.indent}}`;
2307
+ return `{${ sep }${ renderedKeys.join(sep) }\n${ env.indent }}`;
2304
2308
  }
2305
2309
 
2306
2310
  let result = renderedKeys.join(' ');
@@ -2317,7 +2321,7 @@ class CsnToCdl {
2317
2321
  * @return {string}
2318
2322
  */
2319
2323
  renderAlias( alias, env ) {
2320
- return ` as ${this.quoteNonIdentifierOrKeyword(alias, env)}`;
2324
+ return ` as ${ this.quoteNonIdentifierOrKeyword(alias, env) }`;
2321
2325
  }
2322
2326
 
2323
2327
  /**
@@ -2337,17 +2341,17 @@ class CsnToCdl {
2337
2341
  if (!noShortVersion) {
2338
2342
  // Special cases for 1 or 2 arguments.
2339
2343
  if (params.length === 1 && artWithType.length !== undefined)
2340
- return `(${artWithType.length})`;
2344
+ return `(${ artWithType.length })`;
2341
2345
  if (params.length === 2 && artWithType.precision !== undefined && artWithType.scale !== undefined)
2342
- return `(${artWithType.precision}, ${artWithType.scale})`;
2346
+ return `(${ artWithType.precision }, ${ artWithType.scale })`;
2343
2347
  }
2344
2348
 
2345
2349
  // Render named params
2346
2350
  const renderedParams = [];
2347
2351
  for (const param of params)
2348
- renderedParams.push(`${param}: ${artWithType[param]}`);
2352
+ renderedParams.push(`${ param }: ${ artWithType[param] }`);
2349
2353
 
2350
- return `(${renderedParams.join(', ')})`;
2354
+ return `(${ renderedParams.join(', ') })`;
2351
2355
  }
2352
2356
 
2353
2357
  /**
@@ -2386,7 +2390,7 @@ class CsnToCdl {
2386
2390
  const variant = parts.length > 1 ? parts.slice(1).join('#') : undefined;
2387
2391
  const { parentheses } = config;
2388
2392
 
2389
- let result = `${env.indent}@`;
2393
+ let result = `${ env.indent }@`;
2390
2394
  if (parentheses)
2391
2395
  result += '(';
2392
2396
 
@@ -2399,13 +2403,13 @@ class CsnToCdl {
2399
2403
  if (variant !== undefined && variant !== '') {
2400
2404
  // Unfortunately, the compiler does not allow `.@` after the first variant identifier,
2401
2405
  // nor multiple `#`, so we're back at simple paths that are possibly quoted.
2402
- result += `#${this.quotePathIfRequired(variant, env)}`;
2406
+ result += `#${ this.quotePathIfRequired(variant, env) }`;
2403
2407
  }
2404
- result += ` : ${this.renderAnnotationValue(anno, env)}`;
2408
+ result += ` : ${ this.renderAnnotationValue(anno, env) }`;
2405
2409
 
2406
2410
  if (parentheses)
2407
2411
  result += ')';
2408
- return `${result}\n`;
2412
+ return `${ result }\n`;
2409
2413
  }
2410
2414
 
2411
2415
  /**
@@ -2440,7 +2444,7 @@ class CsnToCdl {
2440
2444
  * @return {string}
2441
2445
  */
2442
2446
  renderIncludes( includes, env ) {
2443
- return ` : ${includes.map((name, i) => this.renderDefinitionReference(name, env.withSubPath([ 'includes', i ]))).join(', ')}`;
2447
+ return ` : ${ includes.map((name, i) => this.renderDefinitionReference(name, env.withSubPath([ 'includes', i ]))).join(', ') }`;
2444
2448
  }
2445
2449
 
2446
2450
  createCdlExpressionRenderer() {
@@ -2450,7 +2454,7 @@ class CsnToCdl {
2450
2454
  typeCast(x) {
2451
2455
  const typeRef = that.renderTypeReferenceAndProps(x.cast, this.env.withSubPath([ 'cast' ]), { typeRefOnly: true, noAnnoCollect: true });
2452
2456
  const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
2453
- return `cast(${that.renderArgument(arg, this.env)} as ${typeRef})`;
2457
+ return `cast(${ that.renderArgument(arg, this.env) } as ${ typeRef })`;
2454
2458
  },
2455
2459
  val(x) {
2456
2460
  // Literal value, possibly with explicit 'literal' property
@@ -2463,7 +2467,7 @@ class CsnToCdl {
2463
2467
  case 'date':
2464
2468
  case 'time':
2465
2469
  case 'timestamp':
2466
- return `${x.literal}'${x.val}'`;
2470
+ return `${ x.literal }'${ x.val }'`;
2467
2471
  case 'string':
2468
2472
  return renderString(x.val, this.env);
2469
2473
  case 'object':
@@ -2471,30 +2475,29 @@ class CsnToCdl {
2471
2475
  return 'null';
2472
2476
  // otherwise fall through to
2473
2477
  default:
2474
- throw new ModelError(`Unknown literal or type: ${JSON.stringify(x)}`);
2478
+ throw new ModelError(`Unknown literal or type: ${ JSON.stringify(x) }`);
2475
2479
  }
2476
2480
  },
2477
- aliasOnly: x => x.as,
2478
- enum: x => `#${x['#']}`,
2481
+ enum: x => `#${ x['#'] }`,
2479
2482
  ref(x) {
2480
- return `${x.param ? ':' : ''}${x.ref.map((step, index) => that.renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
2483
+ return `${ x.param ? ':' : '' }${ x.ref.map((step, index) => that.renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.') }`;
2481
2484
  },
2482
2485
  windowFunction(x) {
2483
2486
  const funcDef = this.func(x);
2484
- return `${funcDef} ${this.renderExpr(x.xpr, this.env.withSubPath([ 'xpr' ]))}`; // xpr[0] is 'over'
2487
+ return `${ funcDef } ${ this.renderExpr(x.xpr, this.env.withSubPath([ 'xpr' ])) }`; // xpr[0] is 'over'
2485
2488
  },
2486
2489
  func(x) {
2487
2490
  if (keywords.cdl_functions.includes(x.func.toUpperCase()) && !x.args)
2488
2491
  return x.func;
2489
2492
  const name = that.quoteFunctionIfRequired(x.func, this.env);
2490
2493
  if (!x.args) // e.g. for methods without arguments, `args` is not set at all.
2491
- return `${name}`;
2492
- return `${name}(${that.renderArguments( x, '=>', this.env )})`;
2494
+ return `${ name }`;
2495
+ return `${ name }(${ that.renderArguments( x, '=>', this.env ) })`;
2493
2496
  },
2494
2497
  xpr(x) {
2495
2498
  const xprEnv = this.env.withSubPath([ 'xpr' ]);
2496
2499
  if (this.isNestedXpr && !x.cast)
2497
- return `(${this.renderExpr(x.xpr, xprEnv)})`;
2500
+ return `(${ this.renderExpr(x.xpr, xprEnv) })`;
2498
2501
  return this.renderExpr(x.xpr, xprEnv);
2499
2502
  },
2500
2503
  // Sub-queries in expressions need to be in parentheses, otherwise
@@ -2502,10 +2505,10 @@ class CsnToCdl {
2502
2505
  // For example: `select from E where id in (select from E union select from E);`:
2503
2506
  // Without parentheses, it would be different query.
2504
2507
  SET(x) {
2505
- return `(${that.renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
2508
+ return `(${ that.renderQuery(x, false, 'view', this.env.withIncreasedIndent()) })`;
2506
2509
  },
2507
2510
  SELECT(x) {
2508
- return `(${that.renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
2511
+ return `(${ that.renderQuery(x, false, 'view', this.env.withIncreasedIndent()) })`;
2509
2512
  },
2510
2513
  });
2511
2514
  }
@@ -2633,7 +2636,7 @@ class CsnToCdl {
2633
2636
  quoteAnnotationPathIfRequired( anno, env ) {
2634
2637
  return anno.split('.').map((segment) => {
2635
2638
  if (segment.startsWith('@'))
2636
- return `@${this.quoteNonIdentifier(segment.slice(1), env)}`;
2639
+ return `@${ this.quoteNonIdentifier(segment.slice(1), env) }`;
2637
2640
  return this.quoteNonIdentifier(segment, env);
2638
2641
  }).join('.');
2639
2642
  }
@@ -2667,7 +2670,7 @@ class CdlRenderEnvironment {
2667
2670
  }
2668
2671
 
2669
2672
  increaseIndent() {
2670
- this.indent = ` ${this.indent}`;
2673
+ this.indent = ` ${ this.indent }`;
2671
2674
  }
2672
2675
  decreaseIndent() {
2673
2676
  this.indent = this.indent.substring(0, this.indent.length - INDENT_SIZE);
@@ -2711,7 +2714,7 @@ function removeTrailingNewline( str ) {
2711
2714
 
2712
2715
  /**
2713
2716
  * Returns true if 'id' requires quotes for CDL, i.e. if 'id'
2714
- * does not match the first part of the `Identifier` rule of `language.g4`
2717
+ * does not match the first part of the `ident` rule of `lib/parsers/Lexer.js`
2715
2718
  * or if 'id' is a reserved keyword.
2716
2719
  *
2717
2720
  * Set additionalKeywords to an array of UPPERCASE keywords
@@ -2857,7 +2860,7 @@ function getAllKeywordsForSpecialFunction( funcName ) {
2857
2860
  */
2858
2861
  function renderString( str, env ) {
2859
2862
  if (isSimpleString(str))
2860
- return `'${str.replace(/'/g, '\'\'')}'`;
2863
+ return `'${ str.replace(/'/g, '\'\'') }'`;
2861
2864
 
2862
2865
  // We try to work similar to how JavaScript implements JSON.stringify.
2863
2866
  // JSON.stringify() also checks for unpaired unicode surrogates (see §25.5.2.2,
@@ -2892,17 +2895,17 @@ function renderString( str, env ) {
2892
2895
  // are more than three lines, text blocks with indentation "look nicer".
2893
2896
  // This value was chosen by personal taste.
2894
2897
  if (lines.length > 3) {
2895
- str = lines.join(`\n${env.indent}`);
2896
- return `\`\`\`\n${env.indent}${str}\n${env.indent}\`\`\``;
2898
+ str = lines.join(`\n${ env.indent }`);
2899
+ return `\`\`\`\n${ env.indent }${ str }\n${ env.indent }\`\`\``;
2897
2900
  }
2898
2901
 
2899
- return `\`${str}\``;
2902
+ return `\`${ str }\``;
2900
2903
  }
2901
2904
 
2902
2905
  /** @param {number} codePoint */
2903
2906
  function hexEscape( codePoint ) {
2904
2907
  const hex = codePoint.toString(16);
2905
- return `\\u{${hex}}`;
2908
+ return `\\u{${ hex }}`;
2906
2909
  }
2907
2910
 
2908
2911
  /**
@@ -2933,7 +2936,7 @@ function isSimpleString( str ) {
2933
2936
  * @returns {string}
2934
2937
  */
2935
2938
  function apiDelimitedId( id ) {
2936
- return `![${id.replace(/]/g, ']]')}]`;
2939
+ return `![${ id.replace(/]/g, ']]') }]`;
2937
2940
  }
2938
2941
 
2939
2942
  /**