@abaplint/transpiler-cli 2.13.64 → 2.13.66

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/build/bundle.js CHANGED
@@ -402,7 +402,12 @@ function loadPlugin() {
402
402
  }
403
403
  async function build(config, files) {
404
404
  const libFiles = await loadLib(config);
405
- const t = new Transpiler.Transpiler(config.options, loadPlugin());
405
+ const options = { ...config.options };
406
+ if (config.write_source_map !== true) {
407
+ // Do not pay to allocate and copy mappings that the CLI will not write.
408
+ options.ignoreSourceMap = true;
409
+ }
410
+ const t = new Transpiler.Transpiler(options, loadPlugin());
406
411
  const reg = new abaplint.Registry();
407
412
  for (const f of files) {
408
413
  reg.addFile(new abaplint.MemoryFile(f.filename, f.contents));
@@ -410,7 +415,6 @@ async function build(config, files) {
410
415
  for (const l of libFiles) {
411
416
  reg.addDependency(new abaplint.MemoryFile(l.filename, l.contents));
412
417
  }
413
- reg.parse();
414
418
  const output = await t.run(reg, new Progress());
415
419
  return output;
416
420
  }
@@ -9439,21 +9443,21 @@ class SQLFunction extends combi_1.Expression {
9439
9443
  const fn = (name, params, v) => {
9440
9444
  const nameReg = (0, combi_1.regex)(new RegExp("^" + name + "$", "i"));
9441
9445
  if (params === 0) {
9442
- return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), (0, combi_1.tok)(tokens_1.WParenRightW)));
9446
+ return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), (0, combi_1.tok)(tokens_1.WParenRightW)), { also: combi_1.AlsoIn.OpenABAP });
9443
9447
  }
9444
9448
  else if (params === VARIADIC) {
9445
- return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), sql_function_input_1.SQLFunctionInput, (0, combi_1.starPrio)(commaParam), (0, combi_1.tok)(tokens_1.WParenRightW)));
9449
+ return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), sql_function_input_1.SQLFunctionInput, (0, combi_1.starPrio)(commaParam), (0, combi_1.tok)(tokens_1.WParenRightW)), { also: combi_1.AlsoIn.OpenABAP });
9446
9450
  }
9447
9451
  else {
9448
9452
  const extra = [];
9449
9453
  for (let i = 1; i < params; i++) {
9450
9454
  extra.push(commaParam);
9451
9455
  }
9452
- return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), sql_function_input_1.SQLFunctionInput, ...extra, (0, combi_1.tok)(tokens_1.WParenRightW)));
9456
+ return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), sql_function_input_1.SQLFunctionInput, ...extra, (0, combi_1.tok)(tokens_1.WParenRightW)), { also: combi_1.AlsoIn.OpenABAP });
9453
9457
  }
9454
9458
  };
9455
9459
  const castInput = (0, combi_1.altPrio)(sql_case_1.SQLCase, sql_function_input_1.SQLFunctionInput);
9456
- const cast = (0, combi_1.ver)(version_1.Release.v750, (0, combi_1.seq)((0, combi_1.regex)(/^cast$/i), (0, combi_1.tok)(tokens_1.ParenLeftW), castInput, "AS", castTypes, (0, combi_1.tok)(tokens_1.WParenRightW)));
9460
+ const cast = (0, combi_1.ver)(version_1.Release.v750, (0, combi_1.seq)((0, combi_1.regex)(/^cast$/i), (0, combi_1.tok)(tokens_1.ParenLeftW), castInput, "AS", castTypes, (0, combi_1.tok)(tokens_1.WParenRightW)), { also: combi_1.AlsoIn.OpenABAP });
9457
9461
  const hostParen = (0, combi_1.seq)((0, combi_1.tok)(tokens_1.ParenLeftW), sql_function_input_1.SQLFunctionInput, (0, combi_1.tok)(tokens_1.WParenRightW));
9458
9462
  const hostVar = (0, combi_1.seq)((0, combi_1.tok)(tokens_1.WAt), (0, combi_1.altPrio)(simple_source3_1.SimpleSource3, hostParen));
9459
9463
  const abapOnly = (0, combi_1.altPrio)(constant_1.Constant, hostVar);
@@ -9463,7 +9467,7 @@ class SQLFunction extends combi_1.Expression {
9463
9467
  const kvs = params.map(p => (0, combi_1.seq)((0, combi_1.regex)(new RegExp("^" + p.name + "$", "i")), "=", valueFor(p)));
9464
9468
  const anyKv = kvs.length === 1 ? kvs[0] : (0, combi_1.altPrio)(kvs[0], kvs[1], ...kvs.slice(2));
9465
9469
  const body = (0, combi_1.optPrio)((0, combi_1.seq)(anyKv, (0, combi_1.starPrio)((0, combi_1.seq)(",", anyKv))));
9466
- return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), body, (0, combi_1.tok)(tokens_1.WParenRightW)));
9470
+ return (0, combi_1.ver)(v, (0, combi_1.seq)(nameReg, (0, combi_1.tok)(tokens_1.ParenLeftW), body, (0, combi_1.tok)(tokens_1.WParenRightW)), { also: combi_1.AlsoIn.OpenABAP });
9467
9471
  };
9468
9472
  return (0, combi_1.altPrio)(cast, sql_replace_regexpr_1.SQLReplaceRegexpr, sql_occurrences_regexpr_1.SQLOccurrencesRegexpr, sql_substring_regexpr_1.SQLSubstringRegexpr, sql_locate_regexpr_1.SQLLocateRegexpr, ...exports.sqlFunctionDefs.map(([name, params, v]) => fn(name, params, v)), ...exports.sqlNamedFunctionDefs.map(([name, v, params]) => namedFn(name, v, params)), ...exports.sqlSpatialFunctionDefs.map(([name, params, v]) => fn(name, params, v)), ...exports.sqlSpatialAggregateDefs.map(([name, params, v]) => fn(name, params, v)));
9469
9473
  }
@@ -11249,6 +11253,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
11249
11253
  exports.TypeParam = void 0;
11250
11254
  const combi_1 = __webpack_require__(/*! ../combi */ "./node_modules/@abaplint/core/build/src/abap/2_statements/combi.js");
11251
11255
  const _1 = __webpack_require__(/*! . */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js");
11256
+ const version_1 = __webpack_require__(/*! ../../../version */ "./node_modules/@abaplint/core/build/src/version.js");
11257
+ const _derived_types_1 = __webpack_require__(/*! ./_derived_types */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/_derived_types.js");
11252
11258
  class TypeParam extends combi_1.Expression {
11253
11259
  getRunnable() {
11254
11260
  const table = (0, combi_1.seq)((0, combi_1.altPrio)("STANDARD", "HASHED", "INDEX", "SORTED", "ANY"), "TABLE");
@@ -11256,7 +11262,10 @@ class TypeParam extends combi_1.Expression {
11256
11262
  const typeLine = "LINE OF";
11257
11263
  const ret = (0, combi_1.seq)((0, combi_1.alt)(foo, typeLine), _1.TypeNameOrInfer, (0, combi_1.opt)(_1.Default));
11258
11264
  const like = (0, combi_1.seq)("LIKE", (0, combi_1.opt)("LINE OF"), _1.FieldChain, (0, combi_1.optPrio)(_1.Default));
11259
- return (0, combi_1.alt)((0, combi_1.seq)("TYPE", (0, combi_1.alt)(table, ret)), like);
11265
+ const entity = (0, combi_1.alt)(_1.TypeName, _1.EntityAssociation);
11266
+ const rapTable = (0, combi_1.ver)(version_1.Release.v770, (0, combi_1.seq)("TYPE", "TABLE FOR", (0, _derived_types_1.derivedTypesAlt)((0, combi_1.ver)(version_1.Release.v773, (0, combi_1.seq)("ACTION IMPORT", entity), { also: combi_1.AlsoIn.OpenABAP }), (0, combi_1.ver)(version_1.Release.v774, (0, combi_1.seq)("READ LINK", entity), { also: combi_1.AlsoIn.OpenABAP }), (0, combi_1.ver)(version_1.Release.v787, (0, combi_1.seq)("EVENT", entity), { also: combi_1.AlsoIn.OpenABAP })), (0, combi_1.optPrio)("VALUE IS INITIAL")), { also: combi_1.AlsoIn.OpenABAP });
11267
+ // Method parameters also allow the RAP table and response type forms.
11268
+ return (0, combi_1.altPrio)(rapTable, _1.TypeStructure, (0, combi_1.seq)("TYPE", (0, combi_1.alt)(table, ret)), like);
11260
11269
  }
11261
11270
  }
11262
11271
  exports.TypeParam = TypeParam;
@@ -26522,6 +26531,21 @@ class CurrentScope {
26522
26531
  }
26523
26532
  this.current.getData().forms.push(...f);
26524
26533
  }
26534
+ /** The FORM definitions are built up front, at that point the program level types are not known.
26535
+ * When the FORM statement is traversed it is replaced with one resolved in the scope of the FORM */
26536
+ updateFormDefinition(f) {
26537
+ let search = this.current;
26538
+ while (search !== undefined) {
26539
+ const forms = search.getData().forms;
26540
+ for (let i = 0; i < forms.length; i++) {
26541
+ if (forms[i].equals(f)) {
26542
+ forms[i] = f;
26543
+ return;
26544
+ }
26545
+ }
26546
+ search = search.getParent();
26547
+ }
26548
+ }
26525
26549
  addInterfaceDefinition(i) {
26526
26550
  if (this.current === undefined) {
26527
26551
  return;
@@ -27468,6 +27492,7 @@ class Procedural {
27468
27492
  scope: _current_scope_1.CurrentScope.buildDefault(this.reg, obj),
27469
27493
  filename: file.getFilename(),
27470
27494
  issues: [],
27495
+ deferred: [],
27471
27496
  };
27472
27497
  for (const found of structure.findAllStructures(Structures.Form)) {
27473
27498
  this.scope.addFormDefinitions([new types_1.FormDefinition(found, input)]);
@@ -36913,6 +36938,7 @@ class FindGlobalDefinitions {
36913
36938
  filename: file.getFilename(),
36914
36939
  scope: _current_scope_1.CurrentScope.buildDefault(this.reg, obj),
36915
36940
  issues: [],
36941
+ deferred: [],
36916
36942
  };
36917
36943
  if (obj instanceof interface_1.Interface) {
36918
36944
  const found = struc.findFirstStructure(Structures.Interface);
@@ -41141,6 +41167,9 @@ class Form {
41141
41167
  }
41142
41168
  input.scope.push(_scope_type_1.ScopeType.Form, name, node.getFirstToken().getStart(), input.filename);
41143
41169
  const form = new form_definition_1.FormDefinition(node, input);
41170
+ // the definitions are built up front, before the program level types are known, replace it
41171
+ // with this one, which is resolved in the scope of the FORM
41172
+ input.scope.updateFormDefinition(form);
41144
41173
  input.scope.addList(form.getUsingParameters());
41145
41174
  input.scope.addList(form.getChangingParameters());
41146
41175
  input.scope.addList(form.getTablesParameters());
@@ -44175,12 +44204,19 @@ class Perform {
44175
44204
  input.scope.addReference(expr.getFirstToken(), found, _reference_1.ReferenceType.FormReference, input.filename);
44176
44205
  ////////////////////////////
44177
44206
  // check parameters match
44178
- // USING and CHANGING are interchangeable, the actual parameters form a single positional list
44179
- if (this.checkParameters(node, tables, found.getTablesParameters(), "TABLES", input) === false) {
44180
- return;
44181
- }
44182
- const formal = found.getUsingParameters().concat(found.getChangingParameters());
44183
- this.checkParameters(node, using.concat(changing), formal, "USING/CHANGING", input);
44207
+ // deferred, the FORM might be defined after the PERFORM, its parameters are only typed
44208
+ // once the FORM statement itself has been traversed
44209
+ input.deferred.push(() => {
44210
+ var _a;
44211
+ // look up again, the definition is replaced when the FORM statement is traversed
44212
+ const def = (_a = input.scope.findFormDefinition(name)) !== null && _a !== void 0 ? _a : found;
44213
+ if (this.checkParameters(node, tables, def.getTablesParameters(), "TABLES", input) === false) {
44214
+ return;
44215
+ }
44216
+ // USING and CHANGING are interchangeable, the actual parameters form a single positional list
44217
+ const formal = def.getUsingParameters().concat(def.getChangingParameters());
44218
+ this.checkParameters(node, using.concat(changing), formal, "USING/CHANGING", input);
44219
+ });
44184
44220
  }
44185
44221
  // returns false if an issue was reported
44186
44222
  checkParameters(node, actual, formal, kind, input) {
@@ -45576,22 +45612,8 @@ class SelectOption {
45576
45612
  return;
45577
45613
  }
45578
45614
  const nameChain = node.findFirstExpression(Expressions.FieldChain);
45579
- let found = new basic_types_1.BasicTypes(input).resolveLikeName(nameChain);
45615
+ const found = new basic_types_1.BasicTypes(input).resolveLikeName(nameChain);
45580
45616
  if (found) {
45581
- if (found instanceof basic_1.StructureType) {
45582
- let length = 0;
45583
- for (const c of found.getComponents()) {
45584
- if (c.type instanceof basic_1.CharacterType) {
45585
- length += c.type.getLength();
45586
- }
45587
- }
45588
- if (length === 0) {
45589
- found = basic_1.VoidType.get("Selectoption, fallback");
45590
- }
45591
- else {
45592
- found = new basic_1.CharacterType(length);
45593
- }
45594
- }
45595
45617
  const stru = new basic_1.StructureType([
45596
45618
  { name: "SIGN", type: new basic_1.CharacterType(1) },
45597
45619
  { name: "OPTION", type: new basic_1.CharacterType(2) },
@@ -48845,6 +48867,7 @@ class SyntaxLogic {
48845
48867
  constructor(reg, object) {
48846
48868
  this.reg = reg;
48847
48869
  this.issues = [];
48870
+ this.deferred = [];
48848
48871
  this.object = object;
48849
48872
  }
48850
48873
  run() {
@@ -48858,6 +48881,7 @@ class SyntaxLogic {
48858
48881
  proc: new _procedural_1.Procedural(this.reg, this.scope),
48859
48882
  };
48860
48883
  this.issues = [];
48884
+ this.deferred = [];
48861
48885
  this.reg.getDDICReferences().clear(this.object);
48862
48886
  this.reg.getMSAGReferences().clear(this.object);
48863
48887
  if (this.object instanceof objects_1.Program && this.object.isInclude()) {
@@ -48865,6 +48889,11 @@ class SyntaxLogic {
48865
48889
  return { issues: [], spaghetti: this.scope.pop(new position_1.Position(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)) };
48866
48890
  }
48867
48891
  this.traverseObject();
48892
+ // the FORM definitions are now all traversed, ie. their parameters are typed
48893
+ for (const d of this.deferred) {
48894
+ d();
48895
+ }
48896
+ this.deferred = [];
48868
48897
  for (;;) {
48869
48898
  const spaghetti = this.scope.pop(new position_1.Position(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)); // pop built-in scopes
48870
48899
  if (spaghetti.getTop().getIdentifier().stype === _scope_type_1.ScopeType.BuiltIn) {
@@ -48964,6 +48993,7 @@ class SyntaxLogic {
48964
48993
  scope: this.scope,
48965
48994
  filename,
48966
48995
  issues: this.issues,
48996
+ deferred: this.deferred,
48967
48997
  };
48968
48998
  if (stru instanceof Structures.ClassDefinition) {
48969
48999
  new class_definition_1.ClassDefinition(node, input);
@@ -49015,6 +49045,7 @@ class SyntaxLogic {
49015
49045
  scope: this.scope,
49016
49046
  filename,
49017
49047
  issues: this.issues,
49048
+ deferred: this.deferred,
49018
49049
  };
49019
49050
  // todo, refactor
49020
49051
  if (s instanceof Statements.Type) {
@@ -70595,7 +70626,7 @@ class Registry {
70595
70626
  }
70596
70627
  static abaplintVersion() {
70597
70628
  // magic, see build script "version.js"
70598
- return "2.120.37";
70629
+ return "2.120.42";
70599
70630
  }
70600
70631
  getDDICReferences() {
70601
70632
  return this.ddicReferences;
@@ -73286,7 +73317,7 @@ const KNOWN_CURR_DATA_ELEMENTS = ["BWERT", "DZWERT"];
73286
73317
  const RESERVED_ELEMENT_NAMES = ["BEGIN", "NUMBER", "POSITION"];
73287
73318
  const MAX_LABEL_LENGTH = 40;
73288
73319
  // Annotations which are not supported in CDS view entities
73289
- const VIEW_ENTITY_FORBIDDEN_ANNOTATIONS = ["Semantics.unitOfMeasure"];
73320
+ const VIEW_ENTITY_FORBIDDEN_ANNOTATIONS = ["Semantics.unitOfMeasure", "Semantics.currencyCode"];
73290
73321
  class CDSCheckSyntax {
73291
73322
  constructor() {
73292
73323
  this.conf = new CDSCheckSyntaxConf();
@@ -73330,6 +73361,7 @@ Missing objects are only reported when their names match the configured errorNam
73330
73361
  this.checkFieldAnnotations(tree, ddic, issues, file);
73331
73362
  this.checkLabels(tree, issues, file);
73332
73363
  this.checkViewEntityAnnotations(tree, issues, file);
73364
+ this.checkSearchable(tree, issues, file);
73333
73365
  this.checkReservedNames(tree, issues, file);
73334
73366
  this.checkFields(tree, sources, object, issues, file);
73335
73367
  return issues;
@@ -73551,6 +73583,29 @@ Missing objects are only reported when their names match the configured errorNam
73551
73583
  }
73552
73584
  }
73553
73585
  }
73586
+ checkSearchable(tree, issues, file) {
73587
+ let searchable;
73588
+ for (const annotation of tree.findAllExpressionsRecursive(expressions_1.CDSAnnotation)) {
73589
+ const normalized = annotation.concatTokens().replace(/\s/g, "").toUpperCase().replace(/^@</, "@");
73590
+ if (this.hasSearchAnnotation(normalized, "defaultSearchElement")) {
73591
+ return;
73592
+ }
73593
+ else if (searchable === undefined && this.hasSearchAnnotation(normalized, "searchable")) {
73594
+ searchable = annotation;
73595
+ }
73596
+ }
73597
+ if (searchable !== undefined) {
73598
+ const message = "@Search.defaultSearchElement: true required on at least one element " +
73599
+ "when @Search.searchable: true";
73600
+ issues.push(issue_1.Issue.atToken(file, searchable.getFirstToken(), message, this.getMetadata().key, this.conf.severity));
73601
+ }
73602
+ }
73603
+ hasSearchAnnotation(normalized, name) {
73604
+ if (normalized.startsWith("@SEARCH") === false) {
73605
+ return false;
73606
+ }
73607
+ return new RegExp("[.{,]" + name.toUpperCase() + ":TRUE").test(normalized);
73608
+ }
73554
73609
  checkReservedNames(tree, issues, file) {
73555
73610
  var _a, _b;
73556
73611
  for (const element of tree.findAllExpressionsRecursive(expressions_1.CDSElement)) {
@@ -74242,6 +74297,7 @@ https://help.sap.com/doc/abapdocu_latest_index_htm/latest/en-US/abenchained_stat
74242
74297
  || statement instanceof Statements.Ranges
74243
74298
  || statement instanceof Statements.TypePools
74244
74299
  || statement instanceof Statements.FieldSymbol
74300
+ || statement instanceof Statements.Nodes
74245
74301
  || statement instanceof Statements.Data
74246
74302
  || statement instanceof Statements.DataBegin
74247
74303
  || statement instanceof Statements.DataEnd)) {
@@ -101138,6 +101194,7 @@ exports.DECLARATION_STUFF = exports.SELECTION_EVENTS = void 0;
101138
101194
  const Statements = __importStar(__webpack_require__(/*! ./abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
101139
101195
  exports.SELECTION_EVENTS = [
101140
101196
  Statements.StartOfSelection,
101197
+ Statements.Get,
101141
101198
  Statements.AtSelectionScreen,
101142
101199
  Statements.AtLineSelection,
101143
101200
  Statements.AtPF,
@@ -102234,7 +102291,7 @@ class Chunk {
102234
102291
  if (input === "") {
102235
102292
  return this;
102236
102293
  }
102237
- if (pos && input !== "\n") {
102294
+ if (pos && input !== "\n" && traversal.isSourceMapEnabled?.() !== false) {
102238
102295
  this.mappings.push({
102239
102296
  source: traversal.getFilename(),
102240
102297
  generated: {
@@ -102253,7 +102310,7 @@ class Chunk {
102253
102310
  * already carries mappings, so it never overrides finer-grained mappings.
102254
102311
  */
102255
102312
  ensureStartMapping(pos, traversal) {
102256
- if (this.raw === "" || this.mappings.length > 0) {
102313
+ if (this.raw === "" || this.mappings.length > 0 || traversal.isSourceMapEnabled?.() === false) {
102257
102314
  return this;
102258
102315
  }
102259
102316
  this.mappings.push({
@@ -102296,29 +102353,32 @@ class Chunk {
102296
102353
  if (ignoreSourceMap === true) {
102297
102354
  this.mappings = [];
102298
102355
  }
102356
+ const indentationByLine = this.mappings.length > 0 ? [] : undefined;
102299
102357
  for (const l of this.raw.split("\n")) {
102300
102358
  if (l.startsWith("}")) {
102301
102359
  i = i - 1;
102302
102360
  }
102303
102361
  // clamp so unbalanced braces never produce a negative indent/shift
102304
102362
  const indent = i > 0 ? i * 2 : 0;
102363
+ if (indentationByLine !== undefined) {
102364
+ indentationByLine[line] = indent;
102365
+ }
102305
102366
  if (indent > 0) {
102306
102367
  output.push(" ".repeat(indent) + l);
102307
102368
  }
102308
102369
  else {
102309
102370
  output.push(l);
102310
102371
  }
102311
- // fix maps: shift columns by the indentation actually applied to this line
102312
- for (const m of this.mappings) {
102313
- if (m.generated.line === line) {
102314
- m.generated.column += indent;
102315
- }
102316
- }
102317
102372
  if (l.endsWith(" {")) {
102318
102373
  i = i + 1;
102319
102374
  }
102320
102375
  line++;
102321
102376
  }
102377
+ // Shift every mapping once. The previous line-major implementation
102378
+ // inspected every mapping for every generated line (O(lines * mappings)).
102379
+ for (const m of this.mappings) {
102380
+ m.generated.column += indentationByLine?.[m.generated.line] || 0;
102381
+ }
102322
102382
  this.raw = output.join("\n");
102323
102383
  // line structure is unchanged, but the last line may have been indented
102324
102384
  this.lastLineLength = output[output.length - 1].length;
@@ -102445,39 +102505,49 @@ class DatabaseSetup {
102445
102505
  // note: avoid hitting maximum statement size by splitting into multiple statements
102446
102506
  const insert = [];
102447
102507
  const populateTables = new populate_tables_1.PopulateTables(this.reg);
102508
+ const add = (statement) => {
102509
+ if (statement.length > 0) {
102510
+ insert.push(statement);
102511
+ }
102512
+ };
102513
+ const addAll = (statements) => {
102514
+ for (const statement of statements) {
102515
+ add(statement);
102516
+ }
102517
+ };
102448
102518
  // INSERT data
102449
102519
  for (const obj of this.reg.getObjects()) {
102450
102520
  if (options?.populateTables?.tadir !== false) {
102451
- insert.push(populateTables.insertTADIR(obj));
102521
+ add(populateTables.insertTADIR(obj));
102452
102522
  }
102453
102523
  if (obj instanceof abaplint.Objects.MessageClass) {
102454
- insert.push(...populateTables.insertT100(obj));
102524
+ addAll(populateTables.insertT100(obj));
102455
102525
  }
102456
102526
  else if (options?.populateTables?.wwwparams !== false
102457
102527
  && obj instanceof abaplint.Objects.WebMIME) {
102458
- insert.push(...populateTables.insertWWWPARAMS(obj));
102528
+ addAll(populateTables.insertWWWPARAMS(obj));
102459
102529
  }
102460
102530
  else if (obj instanceof abaplint.Objects.Class
102461
102531
  || obj instanceof abaplint.Objects.Interface
102462
102532
  || obj instanceof abaplint.Objects.Program) {
102463
102533
  if (options?.populateTables?.reposrc !== false) {
102464
- insert.push(populateTables.insertREPOSRC(obj));
102534
+ add(populateTables.insertREPOSRC(obj));
102465
102535
  }
102466
102536
  if ((obj instanceof abaplint.Objects.Class || obj instanceof abaplint.Objects.Interface)
102467
102537
  && options?.populateTables?.seosubco !== false) {
102468
- insert.push(...populateTables.insertSEOSUBCO(obj));
102538
+ addAll(populateTables.insertSEOSUBCO(obj));
102469
102539
  }
102470
102540
  if ((obj instanceof abaplint.Objects.Class || obj instanceof abaplint.Objects.Interface)
102471
102541
  && options?.populateTables?.seosubcodf !== false) {
102472
- insert.push(...populateTables.insertSEOSUBCODF(obj));
102542
+ addAll(populateTables.insertSEOSUBCODF(obj));
102473
102543
  }
102474
102544
  if ((obj instanceof abaplint.Objects.Class || obj instanceof abaplint.Objects.Interface)
102475
102545
  && options?.populateTables?.seosubcotx !== false) {
102476
- insert.push(...populateTables.insertSEOSUBCOTX(obj));
102546
+ addAll(populateTables.insertSEOSUBCOTX(obj));
102477
102547
  }
102478
102548
  }
102479
102549
  }
102480
- insert.push(populateTables.insertT000());
102550
+ add(populateTables.insertT000());
102481
102551
  return insert;
102482
102552
  }
102483
102553
  }
@@ -109790,11 +109860,13 @@ class Transpiler {
109790
109860
  // workaround for web/webpack
109791
109861
  async runRaw(files) {
109792
109862
  const memory = files.map(f => new abaplint.MemoryFile(f.filename, f.contents));
109793
- const reg = new abaplint.Registry().addFiles(memory).parse();
109794
- return new Transpiler().run(reg);
109863
+ const reg = new abaplint.Registry().addFiles(memory);
109864
+ return this.run(reg);
109795
109865
  }
109796
109866
  async run(reg, progress) {
109797
- reg.parse();
109867
+ // Validation installs the transpiler configuration and findIssues() parses
109868
+ // dirty registries. Parsing before that would be wasted because setConfig()
109869
+ // marks every registry object dirty again.
109798
109870
  this.validate(reg);
109799
109871
  const dbSetup = new db_1.DatabaseSetup(reg).run(this.options);
109800
109872
  this.plugin?.amendDatabaseSetup?.(dbSetup, reg, this.options || {});
@@ -110880,7 +110952,14 @@ class AssignTranspiler {
110880
110952
  }
110881
110953
  else {
110882
110954
  let dynamicName = "";
110883
- for (const c of assignSource?.getChildren() || []) {
110955
+ const first = assignSource?.getFirstChild();
110956
+ // dynamicSource below already contains the first source expression.
110957
+ // Keep only the dynamic suffix in dynamicName, otherwise structure
110958
+ // components in that expression are resolved a second time at runtime.
110959
+ const dynamicNameChildren = first?.get() instanceof abaplint.Expressions.Source
110960
+ ? assignSource?.getChildren().slice(1)
110961
+ : assignSource?.getChildren();
110962
+ for (const c of dynamicNameChildren || []) {
110884
110963
  if (c instanceof abaplint.Nodes.ExpressionNode
110885
110964
  && c.get() instanceof abaplint.Expressions.Dynamic
110886
110965
  && c.findFirstExpression(abaplint.Expressions.ConstantString)) {
@@ -110901,7 +110980,10 @@ class AssignTranspiler {
110901
110980
  continue;
110902
110981
  }
110903
110982
  else if (c.concatTokens() === "=>" || c.concatTokens() === "->") {
110904
- dynamicName += " + '" + c.concatTokens() + "'";
110983
+ if (dynamicName !== "") {
110984
+ dynamicName += " + ";
110985
+ }
110986
+ dynamicName += "'" + c.concatTokens() + "'";
110905
110987
  }
110906
110988
  else {
110907
110989
  if (dynamicName !== "") {
@@ -110912,7 +110994,6 @@ class AssignTranspiler {
110912
110994
  }
110913
110995
  options.push(`dynamicName: ` + dynamicName);
110914
110996
  // dynamicSource is the first part of the dynamic part
110915
- const first = assignSource?.getFirstChild();
110916
110997
  if (first?.get() instanceof abaplint.Expressions.Dynamic && first instanceof abaplint.Nodes.ExpressionNode) {
110917
110998
  const firstFirst = first.getChildren()[1];
110918
110999
  if (firstFirst?.get() instanceof abaplint.Expressions.Constant) {
@@ -116549,6 +116630,7 @@ exports.OpenCursorTranspiler = void 0;
116549
116630
  const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js"));
116550
116631
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
116551
116632
  const insert_database_1 = __webpack_require__(/*! ./insert_database */ "./node_modules/@abaplint/transpiler/build/src/statements/insert_database.js");
116633
+ const expressions_1 = __webpack_require__(/*! ../expressions */ "./node_modules/@abaplint/transpiler/build/src/expressions/index.js");
116552
116634
  class OpenCursorTranspiler {
116553
116635
  transpile(node, traversal) {
116554
116636
  const target = traversal.traverse(node.findDirectExpression(abaplint.Expressions.SQLTarget)).getCode();
@@ -116558,11 +116640,21 @@ class OpenCursorTranspiler {
116558
116640
  select += traversal.traverse(selectExpression?.findDirectExpression(abaplint.Expressions.SQLFrom)).getCode();
116559
116641
  const cond = selectExpression?.findDirectExpression(abaplint.Expressions.SQLCond);
116560
116642
  if (cond) {
116561
- select += "WHERE " + traversal.traverse(node).getCode();
116643
+ select += "WHERE " + new expressions_1.SQLCondTranspiler().transpile(cond, traversal).getCode() + " ";
116644
+ }
116645
+ const upTo = selectExpression?.findDirectExpression(abaplint.Expressions.SQLUpTo);
116646
+ if (upTo) {
116647
+ const source = upTo.findFirstExpression(abaplint.Expressions.SimpleSource3);
116648
+ if (source) {
116649
+ select += `UP TO " + ${new expressions_1.SourceTranspiler(true).transpile(source, traversal).getCode()} + " ROWS `;
116650
+ }
116651
+ else {
116652
+ select += upTo.concatTokens() + " ";
116653
+ }
116562
116654
  }
116563
116655
  const orderBy = selectExpression?.findDirectExpression(abaplint.Expressions.SQLOrderBy);
116564
116656
  if (orderBy) {
116565
- select += "ORDER BY " + traversal.traverse(node).getCode();
116657
+ select += "ORDER BY " + new expressions_1.SQLOrderByTranspiler().transpile(orderBy, traversal).getCode();
116566
116658
  }
116567
116659
  const options = [];
116568
116660
  const connection = node.findDirectExpression(abaplint.Expressions.DatabaseConnection);
@@ -122144,6 +122236,8 @@ class Traversal {
122144
122236
  obj;
122145
122237
  sqlInferredType;
122146
122238
  doOrWhileIndexBackups = new Map();
122239
+ statementsInsideLoop = new WeakSet();
122240
+ enclosingDoOrWhile = new WeakMap();
122147
122241
  reg;
122148
122242
  options;
122149
122243
  constructor(spaghetti, file, obj, reg, options) {
@@ -122152,6 +122246,43 @@ class Traversal {
122152
122246
  this.obj = obj;
122153
122247
  this.reg = reg;
122154
122248
  this.options = options;
122249
+ this.buildStatementContext();
122250
+ }
122251
+ /** Build file-level control-flow context once instead of rescanning all
122252
+ * preceding statements for every DATA, CHECK, EXIT, and RETURN. */
122253
+ buildStatementContext() {
122254
+ const loopStack = [];
122255
+ const doOrWhileStack = [];
122256
+ for (const statement of this.file.getStatements()) {
122257
+ const get = statement.get();
122258
+ if (get instanceof abaplint.Statements.Loop
122259
+ || get instanceof abaplint.Statements.While
122260
+ || get instanceof abaplint.Statements.SelectLoop
122261
+ || get instanceof abaplint.Statements.Do) {
122262
+ loopStack.push(statement);
122263
+ }
122264
+ else if (get instanceof abaplint.Statements.EndLoop
122265
+ || get instanceof abaplint.Statements.EndWhile
122266
+ || get instanceof abaplint.Statements.EndSelect
122267
+ || get instanceof abaplint.Statements.EndDo) {
122268
+ loopStack.pop();
122269
+ }
122270
+ if (loopStack.length > 0) {
122271
+ this.statementsInsideLoop.add(statement);
122272
+ }
122273
+ if (get instanceof abaplint.Statements.While
122274
+ || get instanceof abaplint.Statements.Do) {
122275
+ doOrWhileStack.push(statement);
122276
+ }
122277
+ else if (get instanceof abaplint.Statements.EndWhile
122278
+ || get instanceof abaplint.Statements.EndDo) {
122279
+ doOrWhileStack.pop();
122280
+ }
122281
+ const enclosing = doOrWhileStack[doOrWhileStack.length - 1];
122282
+ if (enclosing !== undefined) {
122283
+ this.enclosingDoOrWhile.set(statement, enclosing);
122284
+ }
122285
+ }
122155
122286
  }
122156
122287
  static escapeNamespace(name) {
122157
122288
  return name?.replace(/\//g, "$");
@@ -122198,6 +122329,9 @@ class Traversal {
122198
122329
  getSpaghetti() {
122199
122330
  return this.spaghetti;
122200
122331
  }
122332
+ isSourceMapEnabled() {
122333
+ return this.options?.ignoreSourceMap !== true;
122334
+ }
122201
122335
  /** finds a statement in the _current_ file given a position */
122202
122336
  findStatementInFile(pos) {
122203
122337
  for (const s of this.file.getStatements()) {
@@ -122660,9 +122794,16 @@ class Traversal {
122660
122794
  return ret;
122661
122795
  }
122662
122796
  buildFriendsAccess(def, hasSuperClass) {
122663
- let ret = "this.FRIENDS_ACCESS_INSTANCE = {\n";
122797
+ let ret = "";
122664
122798
  if (hasSuperClass === true) {
122665
- ret += `"SUPER": sup.FRIENDS_ACCESS_INSTANCE,\n`;
122799
+ // A subclass instance can be held through a superclass reference. In
122800
+ // that case friend access must still find private members declared by
122801
+ // the superclass.
122802
+ ret += "this.FRIENDS_ACCESS_INSTANCE = Object.create(sup.FRIENDS_ACCESS_INSTANCE || null);\n";
122803
+ ret += `this.FRIENDS_ACCESS_INSTANCE["SUPER"] = sup.FRIENDS_ACCESS_INSTANCE;\n`;
122804
+ }
122805
+ else {
122806
+ ret += "this.FRIENDS_ACCESS_INSTANCE = {\n";
122666
122807
  }
122667
122808
  for (const method of def.getMethodDefinitions()?.getAll() || []) {
122668
122809
  const name = method.getName().toLowerCase();
@@ -122675,9 +122816,16 @@ class Traversal {
122675
122816
  }
122676
122817
  const methodName = privateHash + Traversal.escapeNamespace(name.replace("~", "$"));
122677
122818
  // NOTE: currently all are needed in the unit test setup
122678
- ret += `"${name.replace("~", "$")}": this.${methodName}.bind(this),\n`;
122819
+ if (hasSuperClass === true) {
122820
+ ret += `this.FRIENDS_ACCESS_INSTANCE["${name.replace("~", "$")}"] = this.${methodName}.bind(this);\n`;
122821
+ }
122822
+ else {
122823
+ ret += `"${name.replace("~", "$")}": this.${methodName}.bind(this),\n`;
122824
+ }
122825
+ }
122826
+ if (hasSuperClass === false) {
122827
+ ret += "};\n";
122679
122828
  }
122680
- ret += "};\n";
122681
122829
  return ret;
122682
122830
  }
122683
122831
  buildConstructorContents(scope, def) {
@@ -122916,26 +123064,7 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
122916
123064
  return context;
122917
123065
  }
122918
123066
  isInsideLoop(node) {
122919
- const stack = [];
122920
- for (const statement of this.getFile().getStatements()) {
122921
- const get = statement.get();
122922
- if (get instanceof abaplint.Statements.Loop
122923
- || get instanceof abaplint.Statements.While
122924
- || get instanceof abaplint.Statements.SelectLoop
122925
- || get instanceof abaplint.Statements.Do) {
122926
- stack.push(statement);
122927
- }
122928
- else if (get instanceof abaplint.Statements.EndLoop
122929
- || get instanceof abaplint.Statements.EndWhile
122930
- || get instanceof abaplint.Statements.EndSelect
122931
- || get instanceof abaplint.Statements.EndDo) {
122932
- stack.pop();
122933
- }
122934
- if (statement === node) {
122935
- break;
122936
- }
122937
- }
122938
- return stack.length > 0;
123067
+ return this.statementsInsideLoop.has(node);
122939
123068
  }
122940
123069
  isInsideDoOrWhile(node) {
122941
123070
  return this.findCurrentDoOrWhileIndexBackup(node) !== undefined;
@@ -122944,22 +123073,8 @@ this.INTERNAL_ID = abap.internalIdCounter++;\n`;
122944
123073
  this.doOrWhileIndexBackups.set(node, name);
122945
123074
  }
122946
123075
  findCurrentDoOrWhileIndexBackup(node) {
122947
- const stack = [];
122948
- for (const statement of this.getFile().getStatements()) {
122949
- const get = statement.get();
122950
- if (get instanceof abaplint.Statements.While
122951
- || get instanceof abaplint.Statements.Do) {
122952
- stack.push(this.doOrWhileIndexBackups.get(statement));
122953
- }
122954
- else if (get instanceof abaplint.Statements.EndWhile
122955
- || get instanceof abaplint.Statements.EndDo) {
122956
- stack.pop();
122957
- }
122958
- if (statement === node) {
122959
- break;
122960
- }
122961
- }
122962
- return stack[stack.length - 1];
123076
+ const enclosing = this.enclosingDoOrWhile.get(node);
123077
+ return enclosing === undefined ? undefined : this.doOrWhileIndexBackups.get(enclosing);
122963
123078
  }
122964
123079
  registerClassOrInterface(def) {
122965
123080
  if (def === undefined) {
package/build/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ITranspilerOptions } from "@abaplint/transpiler";
2
2
  export interface ITranspilerConfig {
3
+ /** @uniqueItems true */
3
4
  input_folder: string | string[];
4
5
  /** list of regex, case insensitive, empty gives all files, positive list
5
6
  * @uniqueItems true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler-cli",
3
- "version": "2.13.64",
3
+ "version": "2.13.66",
4
4
  "description": "Transpiler - Command Line Interface",
5
5
  "funding": "https://github.com/sponsors/larshp",
6
6
  "bin": {
@@ -27,8 +27,8 @@
27
27
  "author": "abaplint",
28
28
  "license": "MIT",
29
29
  "devDependencies": {
30
- "@abaplint/core": "^2.120.37",
31
- "@abaplint/transpiler": "^2.13.64",
30
+ "@abaplint/core": "^2.120.42",
31
+ "@abaplint/transpiler": "^2.13.66",
32
32
  "@types/glob": "^8.1.0",
33
33
  "@types/node": "^24.13.3",
34
34
  "@types/progress": "^2.0.7",
package/schema.json CHANGED
@@ -32,7 +32,8 @@
32
32
  },
33
33
  "type": "array"
34
34
  }
35
- ]
35
+ ],
36
+ "uniqueItems": true
36
37
  },
37
38
  "libs": {
38
39
  "items": {
@@ -76,14 +77,6 @@
76
77
  "output_folder": {
77
78
  "type": "string"
78
79
  },
79
- "plugins": {
80
- "description": "list of npm module names or paths, resolved from the current working directory, each module exports \"plugins\", an array of ITranspilerPlugin instances",
81
- "items": {
82
- "type": "string"
83
- },
84
- "type": "array",
85
- "uniqueItems": true
86
- },
87
80
  "write_source_map": {
88
81
  "type": "boolean"
89
82
  },