@abaplint/transpiler-cli 2.13.86 → 2.13.88

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 (2) hide show
  1. package/build/bundle.js +681 -43
  2. package/package.json +3 -3
package/build/bundle.js CHANGED
@@ -177,15 +177,32 @@ class FileOperations {
177
177
  }
178
178
  return (0, p_limit_1.default)(concurrency);
179
179
  }
180
+ // abapGit stores the content of a Web Repository or MIME object beside its
181
+ // XML, in a file whose name says .w3mi.data. or .smim.data. That content is
182
+ // bytes, and reading it as UTF-8 destroys it: every byte above 0x7F fails to
183
+ // decode and comes back as the replacement character, so a PNG arrives
184
+ // starting ef bf bd rather than 89 50 4e 47 and is served, with a plausible
185
+ // size and the right content type, as a broken image.
186
+ //
187
+ // latin1 rather than a Buffer because the whole pipeline carries a file as a
188
+ // string; latin1 maps every byte to the code point of the same value, so a
189
+ // read and a write of it are exact. Node's "binary" is the same encoding
190
+ // under its old name, so there is nothing more faithful to switch to: the
191
+ // only stronger answer is to carry a Buffer end to end and never make it a
192
+ // string at all, which is a change to every file the transpiler touches.
193
+ // Measured on an 11770 byte PNG: latin1 and binary both return it
194
+ // unchanged, utf8 returns 20175 bytes and no longer a PNG.
195
+ static isBinaryFilename(filename) {
196
+ return /\.(w3mi|smim)\.data\./i.test(filename);
197
+ }
180
198
  static async readAllFiles(filesToRead, outputFolder) {
181
199
  const limit = this.setupPLimit();
182
200
  const promises = filesToRead.map((filename) => {
183
201
  return limit(async () => {
184
- // const isBinary = filename.includes(".w3mi.data.");
185
202
  return {
186
203
  filename: path.basename(filename),
187
204
  relative: path.relative(outputFolder, path.dirname(filename)),
188
- contents: await fsPromises.readFile(filename, "utf8"),
205
+ contents: await fsPromises.readFile(filename, this.isBinaryFilename(filename) ? "latin1" : "utf8"),
189
206
  };
190
207
  });
191
208
  });
@@ -221,7 +238,9 @@ class FileOperations {
221
238
  const limit = this.setupPLimit();
222
239
  const promises = files.map((file) => {
223
240
  return limit(async () => {
224
- await fsPromises.writeFile(file.path, file.contents);
241
+ // read as latin1, written as latin1: anything else re-encodes the
242
+ // bytes on the way out and undoes the careful read
243
+ await fsPromises.writeFile(file.path, file.contents, this.isBinaryFilename(file.path) ? { encoding: "latin1" } : undefined);
225
244
  });
226
245
  });
227
246
  await Promise.all(promises);
@@ -380,7 +399,7 @@ async function writeObjects(outputFiles, config, outputFolder, files) {
380
399
  && (type === "PROG" || type === "FUGR" || type === "CLAS")) {
381
400
  const name = output.filename + ".map";
382
401
  // SourceMappingUrl needs to be percent-encoded, ref https://github.com/microsoft/TypeScript/issues/40951
383
- contents = contents + `\n//# sourceMappingURL=` + name.replace(/#/g, "%23");
402
+ contents = contents + `\n//# sourceMappingURL=` + name.replace(/%/g, "%25").replace(/#/g, "%23");
384
403
  // map the bare abap filename each mapping carries to its path on disk;
385
404
  // source map "sources" are URLs, so always forward slashes, also on Windows
386
405
  const sourcePaths = {};
@@ -522,6 +541,13 @@ const BUFS = new Set([
522
541
  CH_DOT, CH_COMMA, CH_COLON, 40 /* ( */, 41 /* ) */, 91 /* [ */,
523
542
  93 /* ] */, 43 /* + */, CH_AT
524
543
  ]);
544
+ // characters that may follow the closing "'", "`" or "|" of a literal, anything else
545
+ // is a syntax error, "There must be a space or equivalent character after ...",
546
+ // note that "(" is allowed, ie. text elements like 'text'(001)
547
+ const AFTER_LITERAL = new Set([
548
+ EOF, CH_SPACE, CH_TAB, CH_NL, CH_DOT, CH_COMMA, CH_COLON, CH_DQUOTE,
549
+ 40 /* ( */, 41 /* ) */, 91 /* [ */, 93 /* ] */
550
+ ]);
525
551
  class Lexer {
526
552
  run(file, virtual) {
527
553
  this.virtual = virtual;
@@ -801,7 +827,11 @@ class Lexer {
801
827
  && ahead !== CH_BACKTICK
802
828
  && this.buffer.countIsEven(CH_BACKTICK)) {
803
829
  // end of ping
804
- this.add();
830
+ if (AFTER_LITERAL.has(ahead)) {
831
+ this.add();
832
+ }
833
+ // else: no separator after the literal, keep the buffer so the following
834
+ // characters are glued onto the token, the parser will report the error
805
835
  if (ahead === CH_DQUOTE) {
806
836
  this.m = ModeComment;
807
837
  }
@@ -814,7 +844,9 @@ class Lexer {
814
844
  && (current === CH_PIPE || current === CH_LBRACE)
815
845
  && (stream.prevChar() !== CH_BACKSLASH || (stream.prevPrevChar() === CH_BACKSLASH && stream.prevChar() === CH_BACKSLASH))) {
816
846
  // end of template
817
- this.add();
847
+ if (current === CH_LBRACE || AFTER_LITERAL.has(ahead)) {
848
+ this.add();
849
+ }
818
850
  this.m = ModeNormal;
819
851
  }
820
852
  else if (this.m === ModeTemplate
@@ -829,7 +861,9 @@ class Lexer {
829
861
  && ahead !== CH_QUOTE
830
862
  && this.buffer.countIsEven(CH_QUOTE)) {
831
863
  // end of string
832
- this.add();
864
+ if (AFTER_LITERAL.has(ahead)) {
865
+ this.add();
866
+ }
833
867
  if (ahead === CH_DQUOTE) {
834
868
  this.m = ModeComment;
835
869
  }
@@ -12841,7 +12875,7 @@ class CallTransaction {
12841
12875
  getMatcher() {
12842
12876
  const options = (0, combi_1.seq)("OPTIONS FROM", expressions_1.Source);
12843
12877
  const messages = (0, combi_1.seq)("MESSAGES INTO", expressions_1.Target);
12844
- const auth = (0, combi_1.seq)((0, combi_1.altPrio)("WITH", "WITHOUT"), "AUTHORITY-CHECK");
12878
+ const auth = (0, combi_1.ver)(version_1.Release.v740sp02, (0, combi_1.seq)((0, combi_1.altPrio)("WITH", "WITHOUT"), "AUTHORITY-CHECK"));
12845
12879
  const perm = (0, combi_1.per)((0, combi_1.seq)("UPDATE", expressions_1.Source), "AND SKIP FIRST SCREEN", options, messages, (0, combi_1.seq)("MODE", expressions_1.Source));
12846
12880
  const ret = (0, combi_1.seq)("CALL TRANSACTION", expressions_1.Source, (0, combi_1.optPrio)(auth), (0, combi_1.optPrio)((0, combi_1.seq)("USING", expressions_1.Source)), (0, combi_1.opt)(perm));
12847
12881
  return (0, combi_1.verNotLang)(version_1.LanguageVersion.Cloud, ret);
@@ -25119,12 +25153,15 @@ class ABAPFileInformationParser {
25119
25153
  continue;
25120
25154
  }
25121
25155
  const parameters = this.parseMethodParameters(def);
25156
+ const concatenated = def.concatTokens().toUpperCase();
25122
25157
  methods.push({
25123
25158
  name: methodName.getStr(),
25124
25159
  identifier: new _identifier_1.Identifier(methodName, this.filename),
25125
25160
  isRedefinition: def.findDirectExpression(Expressions.Redefinition) !== undefined,
25126
- isForTesting: def.concatTokens().toUpperCase().includes(" FOR TESTING"),
25127
- isFinal: def.concatTokens().toUpperCase().includes(" FINAL"),
25161
+ isForTesting: concatenated.includes(" FOR TESTING"),
25162
+ isFinal: concatenated.includes(" FINAL"),
25163
+ isDefaultIgnore: concatenated.includes(" DEFAULT IGNORE"),
25164
+ isDefaultFail: concatenated.includes(" DEFAULT FAIL"),
25128
25165
  isAbstract: def.findDirectExpression(Expressions.Abstract) !== undefined,
25129
25166
  isEventHandler: def.findDirectExpression(Expressions.EventHandler) !== undefined,
25130
25167
  visibility,
@@ -28183,6 +28220,20 @@ class TypeUtils {
28183
28220
  || node.findFirstExpression(Expressions.ArithOperator) !== undefined;
28184
28221
  return calculated;
28185
28222
  }
28223
+ // a character constant is only assignable to a hexadecimal target if it can be
28224
+ // interpreted as a hexadecimal value
28225
+ isHexConstant(node) {
28226
+ const constant = node === null || node === void 0 ? void 0 : node.concatTokens();
28227
+ if (constant === undefined) {
28228
+ return true;
28229
+ }
28230
+ if ((constant.startsWith("'") && constant.endsWith("'"))
28231
+ || (constant.startsWith("`") && constant.endsWith("`"))) {
28232
+ const value = constant.substring(1, constant.length - 1);
28233
+ return value.length > 0 && /^[0-9A-Fa-f]+$/.test(value);
28234
+ }
28235
+ return true;
28236
+ }
28186
28237
  isAssignableNew(source, target, node) {
28187
28238
  const calculated = (node === null || node === void 0 ? void 0 : node.findFirstExpression(Expressions.ArithOperator)) !== undefined;
28188
28239
  if (calculated && source instanceof basic_1.HexType && target instanceof basic_1.IntegerType) {
@@ -28248,7 +28299,7 @@ class TypeUtils {
28248
28299
  }
28249
28300
  else if (target instanceof basic_1.XStringType) {
28250
28301
  if (((_d = source.getAbstractTypeData()) === null || _d === void 0 ? void 0 : _d.derivedFromConstant) === true) {
28251
- return (node === null || node === void 0 ? void 0 : node.concatTokens()) !== "''";
28302
+ return this.isHexConstant(node);
28252
28303
  }
28253
28304
  return false;
28254
28305
  }
@@ -35180,6 +35231,7 @@ class Source {
35180
35231
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
35181
35232
  }
35182
35233
  this.addIfInferred(node, input, foundType);
35234
+ this.traverseRemainingChildren(children, input);
35183
35235
  return foundType;
35184
35236
  }
35185
35237
  case "REF":
@@ -35275,11 +35327,15 @@ class Source {
35275
35327
  }
35276
35328
  let hexExpected = false;
35277
35329
  let hexNext = false;
35330
+ // an arithmetic operator has been seen, so the operands that follow
35331
+ // determine a calculation type rather than simply replacing the context
35332
+ let arithmetic = false;
35278
35333
  while (children.length >= 0) {
35279
35334
  if (first instanceof nodes_1.ExpressionNode) {
35280
35335
  const get = first.get();
35281
35336
  if (get instanceof Expressions.MethodCallChain) {
35282
- context = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
35337
+ const found = method_call_chain_1.MethodCallChain.runSyntax(first, input, targetType);
35338
+ context = arithmetic === true ? this.infer(context, found, true) : found;
35283
35339
  if (context === undefined) {
35284
35340
  const message = "Method has no RETURNING value";
35285
35341
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
@@ -35287,18 +35343,19 @@ class Source {
35287
35343
  }
35288
35344
  }
35289
35345
  else if (get instanceof Expressions.FieldChain) {
35290
- context = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
35346
+ const found = field_chain_1.FieldChain.runSyntax(first, input, type, allowGenericDeference);
35347
+ context = arithmetic === true ? this.infer(context, found, true) : found;
35291
35348
  }
35292
35349
  else if (get instanceof Expressions.StringTemplate) {
35293
35350
  context = string_template_1.StringTemplate.runSyntax(first, input);
35294
35351
  }
35295
35352
  else if (get instanceof Expressions.Source) {
35296
35353
  const found = Source.runSyntax(first, input);
35297
- context = this.infer(context, found);
35354
+ context = this.infer(context, found, arithmetic);
35298
35355
  }
35299
35356
  else if (get instanceof Expressions.Constant) {
35300
35357
  const found = constant_1.Constant.runSyntax(first);
35301
- context = this.infer(context, found);
35358
+ context = this.infer(context, found, arithmetic);
35302
35359
  }
35303
35360
  else if (get instanceof Expressions.Dereference) {
35304
35361
  context = dereference_1.Dereference.runSyntax(first, context, input);
@@ -35307,6 +35364,7 @@ class Source {
35307
35364
  context = component_chain_1.ComponentChain.runSyntax(context, first, input);
35308
35365
  }
35309
35366
  else if (get instanceof Expressions.ArithOperator) {
35367
+ arithmetic = true;
35310
35368
  if (first.concatTokens() === "**") {
35311
35369
  context = new basic_1.FloatType();
35312
35370
  }
@@ -35353,7 +35411,13 @@ class Source {
35353
35411
  Source.runSyntax(last, input);
35354
35412
  }
35355
35413
  }
35356
- static infer(context, found) {
35414
+ static infer(context, found, arithmetic = false) {
35415
+ if (arithmetic === true) {
35416
+ const calculation = this.calculationType(context, found);
35417
+ if (calculation !== undefined) {
35418
+ return calculation;
35419
+ }
35420
+ }
35357
35421
  if (context instanceof basic_1.FloatType && found instanceof basic_1.IntegerType) {
35358
35422
  return context;
35359
35423
  }
@@ -35367,6 +35431,56 @@ class Source {
35367
35431
  return found;
35368
35432
  }
35369
35433
  }
35434
+ /** The calculation type of an arithmetic expression is the most general of the
35435
+ * operand types. An operand of type c, n, d, t or string is converted into it and
35436
+ * never becomes the result: "lv_f * '0.25'" is a float, not a character field of
35437
+ * length four. Returns undefined when neither operand is numeric, leaving the
35438
+ * decision to the caller. */
35439
+ static calculationType(left, right) {
35440
+ if (left === undefined || right === undefined) {
35441
+ return undefined;
35442
+ }
35443
+ else if (left instanceof basic_1.VoidType || left instanceof unknown_type_1.UnknownType) {
35444
+ // not knowing an operand means not knowing the result
35445
+ return left;
35446
+ }
35447
+ else if (right instanceof basic_1.VoidType || right instanceof unknown_type_1.UnknownType) {
35448
+ return right;
35449
+ }
35450
+ const leftRank = this.numericRank(left);
35451
+ const rightRank = this.numericRank(right);
35452
+ if (leftRank === undefined && rightRank === undefined) {
35453
+ return undefined;
35454
+ }
35455
+ else if (rightRank === undefined) {
35456
+ return left;
35457
+ }
35458
+ else if (leftRank === undefined) {
35459
+ return right;
35460
+ }
35461
+ return leftRank >= rightRank ? left : right;
35462
+ }
35463
+ static numericRank(type) {
35464
+ if (type instanceof basic_1.DecFloat34Type) {
35465
+ return 6;
35466
+ }
35467
+ else if (type instanceof basic_1.DecFloat16Type) {
35468
+ return 5;
35469
+ }
35470
+ else if (type instanceof basic_1.FloatType) {
35471
+ return 4;
35472
+ }
35473
+ else if (type instanceof basic_1.PackedType) {
35474
+ return 3;
35475
+ }
35476
+ else if (type instanceof basic_1.Integer8Type) {
35477
+ return 2;
35478
+ }
35479
+ else if (type instanceof basic_1.IntegerType) {
35480
+ return 1;
35481
+ }
35482
+ return undefined;
35483
+ }
35370
35484
  static addIfInferred(node, input, inferredType) {
35371
35485
  const basic = new basic_types_1.BasicTypes(input);
35372
35486
  const typeExpression = node.findDirectExpression(Expressions.TypeNameOrInfer);
@@ -71018,7 +71132,7 @@ class Registry {
71018
71132
  }
71019
71133
  static abaplintVersion() {
71020
71134
  // magic, see build script "version.js"
71021
- return "2.120.50";
71135
+ return "2.120.54";
71022
71136
  }
71023
71137
  getDDICReferences() {
71024
71138
  return this.ddicReferences;
@@ -78478,7 +78592,11 @@ Make sure to test the downported code, it might not always be completely correct
78478
78592
  return found;
78479
78593
  }
78480
78594
  */
78481
- let found = this.downportEnum(low, high, lowFile, highSyntax, highFile);
78595
+ let found = this.stripCallTransactionAuthorityCheck(high, lowFile);
78596
+ if (found) {
78597
+ return found;
78598
+ }
78599
+ found = this.downportEnum(low, high, lowFile, highSyntax, highFile);
78482
78600
  if (found) {
78483
78601
  return found;
78484
78602
  }
@@ -79155,15 +79273,19 @@ ${indentation}${uniqueName} = ${source.concatTokens()}.\n${indentation}`);
79155
79273
  continue;
79156
79274
  }
79157
79275
  const condition = this.tableCondition(tableExpression);
79158
- const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
79276
+ // ASSIGNING, not INTO: from v740 a table expression in a read position addresses the
79277
+ // row rather than copying it, so "REF #( tab[ i ]-comp )" and "ASSIGN tab[ i ]-comp"
79278
+ // hand back an address INTO a work area would not preserve. The write path
79279
+ // (moveWithTableTarget) already lowers to ASSIGNING for the same reason.
79280
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax, true);
79159
79281
  const tabixBackup = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
79160
79282
  const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
79161
79283
  const firstToken = high.getFirstToken();
79162
79284
  // note that the tabix restore should be done before throwing the exception
79163
- const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `DATA ${uniqueName} LIKE LINE OF ${pre}.
79285
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, firstToken.getStart(), `FIELD-SYMBOLS ${uniqueName} LIKE LINE OF ${pre}.
79164
79286
  ${indentation}DATA ${tabixBackup} LIKE sy-tabix.
79165
79287
  ${indentation}${tabixBackup} = sy-tabix.
79166
- ${indentation}READ TABLE ${pre} ${condition}INTO ${uniqueName}.
79288
+ ${indentation}READ TABLE ${pre} ${condition}ASSIGNING ${uniqueName}.
79167
79289
  ${indentation}sy-tabix = ${tabixBackup}.
79168
79290
  ${indentation}IF sy-subrc <> 0.
79169
79291
  ${indentation} RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
@@ -79917,8 +80039,7 @@ CONSTANTS: BEGIN OF ${structureName},\n`;
79917
80039
  if (index === undefined) {
79918
80040
  return undefined;
79919
80041
  }
79920
- let uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
79921
- uniqueName = `<${uniqueName}>`;
80042
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax, true);
79922
80043
  const tName = target.concatTokens().split("[")[0];
79923
80044
  const condition = this.tableCondition(tableExpression);
79924
80045
  const tabixBackup = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
@@ -80930,12 +81051,16 @@ ${indentation} output = ${uniqueName}.\n`;
80930
81051
  }
80931
81052
  return undefined;
80932
81053
  }
80933
- uniqueName(position, filename, highSyntax) {
81054
+ /** a FIELD-SYMBOLS declaration is known to the scope under its bracketed spelling, so
81055
+ * the collision check has to use that spelling too - otherwise a second outline in the
81056
+ * same method is handed a name that is already taken and the result does not compile */
81057
+ uniqueName(position, filename, highSyntax, fieldSymbol = false) {
81058
+ const decorate = (name) => fieldSymbol === true ? `<${name}>` : name;
80934
81059
  const spag = highSyntax.spaghetti.lookupPosition(position, filename);
80935
81060
  if (spag === undefined) {
80936
81061
  const name = "temprr" + this.counter;
80937
81062
  this.counter++;
80938
- return name;
81063
+ return decorate(name);
80939
81064
  }
80940
81065
  let postfix = "";
80941
81066
  if (spag.getIdentifier().stype === _scope_type_1.ScopeType.ClassDefinition) {
@@ -80944,7 +81069,7 @@ ${indentation} output = ${uniqueName}.\n`;
80944
81069
  postfix = "_" + hash.substring(0, 10);
80945
81070
  }
80946
81071
  while (true) {
80947
- const name = "temp" + this.counter + postfix;
81072
+ const name = decorate("temp" + this.counter + postfix);
80948
81073
  const exists = this.existsRecursive(spag, name);
80949
81074
  this.counter++;
80950
81075
  if (exists === false) {
@@ -80965,6 +81090,27 @@ ${indentation} output = ${uniqueName}.\n`;
80965
81090
  }
80966
81091
  return false;
80967
81092
  }
81093
+ /** WITH/WITHOUT AUTHORITY-CHECK on CALL TRANSACTION requires v740sp02, so the statement is
81094
+ * Unknown in the low version, note that only WITHOUT is handled here */
81095
+ stripCallTransactionAuthorityCheck(high, lowFile) {
81096
+ if (!(high.get() instanceof Statements.CallTransaction)) {
81097
+ return undefined;
81098
+ }
81099
+ const tokens = high.getTokens();
81100
+ // the lexer splits "AUTHORITY-CHECK" into three tokens: AUTHORITY, -, CHECK
81101
+ for (let i = 1; i < tokens.length - 3; i++) {
81102
+ if (tokens[i].getStr().toUpperCase() === "WITHOUT"
81103
+ && tokens[i + 1].getStr().toUpperCase() === "AUTHORITY"
81104
+ && tokens[i + 2].getStr() === "-"
81105
+ && tokens[i + 3].getStr().toUpperCase() === "CHECK") {
81106
+ // also remove the preceding space so no double space is left behind
81107
+ const fix = edit_helper_1.EditHelper.deleteRange(lowFile, tokens[i - 1].getEnd(), tokens[i + 3].getEnd());
81108
+ const message = "Remove WITHOUT AUTHORITY-CHECK from CALL TRANSACTION";
81109
+ return issue_1.Issue.atToken(lowFile, tokens[i], message, this.getMetadata().key, this.conf.severity, fix);
81110
+ }
81111
+ }
81112
+ return undefined;
81113
+ }
80968
81114
  replaceXsdBool(node, lowFile, highSyntax) {
80969
81115
  if (this.lowReg.getConfig().isOpenABAP()) {
80970
81116
  return undefined;
@@ -84618,6 +84764,11 @@ class ImplementMethods extends _abap_rule_1.ABAPRule {
84618
84764
  return [idef];
84619
84765
  }
84620
84766
  for (const m of this.findInterfaceMethods(idef)) {
84767
+ if (m.method.isDefaultIgnore === true || m.method.isDefaultFail === true) {
84768
+ // the interface says an implementation is optional, so asking for one
84769
+ // reports an error where ABAP reports none
84770
+ continue;
84771
+ }
84621
84772
  if (this.isAbstract(m, interfaceInfo, def)) {
84622
84773
  if (def.isAbstract) {
84623
84774
  continue;
@@ -85369,6 +85520,7 @@ __exportStar(__webpack_require__(/*! ./parser_missing_space */ "./node_modules/@
85369
85520
  __exportStar(__webpack_require__(/*! ./pragma_style */ "./node_modules/@abaplint/core/build/src/rules/pragma_style.js"), exports);
85370
85521
  __exportStar(__webpack_require__(/*! ./prefer_corresponding */ "./node_modules/@abaplint/core/build/src/rules/prefer_corresponding.js"), exports);
85371
85522
  __exportStar(__webpack_require__(/*! ./prefer_inline */ "./node_modules/@abaplint/core/build/src/rules/prefer_inline.js"), exports);
85523
+ __exportStar(__webpack_require__(/*! ./prefer_insert_into_table */ "./node_modules/@abaplint/core/build/src/rules/prefer_insert_into_table.js"), exports);
85372
85524
  __exportStar(__webpack_require__(/*! ./prefer_is_not */ "./node_modules/@abaplint/core/build/src/rules/prefer_is_not.js"), exports);
85373
85525
  __exportStar(__webpack_require__(/*! ./prefer_pragmas */ "./node_modules/@abaplint/core/build/src/rules/prefer_pragmas.js"), exports);
85374
85526
  __exportStar(__webpack_require__(/*! ./prefer_raise_exception_new */ "./node_modules/@abaplint/core/build/src/rules/prefer_raise_exception_new.js"), exports);
@@ -85426,6 +85578,7 @@ __exportStar(__webpack_require__(/*! ./use_line_exists */ "./node_modules/@abapl
85426
85578
  __exportStar(__webpack_require__(/*! ./use_new */ "./node_modules/@abaplint/core/build/src/rules/use_new.js"), exports);
85427
85579
  __exportStar(__webpack_require__(/*! ./when_others_last */ "./node_modules/@abaplint/core/build/src/rules/when_others_last.js"), exports);
85428
85580
  __exportStar(__webpack_require__(/*! ./whitespace_end */ "./node_modules/@abaplint/core/build/src/rules/whitespace_end.js"), exports);
85581
+ __exportStar(__webpack_require__(/*! ./wrong_abapdoc_position */ "./node_modules/@abaplint/core/build/src/rules/wrong_abapdoc_position.js"), exports);
85429
85582
  __exportStar(__webpack_require__(/*! ./xml_consistency */ "./node_modules/@abaplint/core/build/src/rules/xml_consistency.js"), exports);
85430
85583
  __exportStar(__webpack_require__(/*! ./no_exclamation_escape */ "./node_modules/@abaplint/core/build/src/rules/no_exclamation_escape.js"), exports);
85431
85584
  __exportStar(__webpack_require__(/*! ./xml_bom */ "./node_modules/@abaplint/core/build/src/rules/xml_bom.js"), exports);
@@ -93047,6 +93200,127 @@ exports.PreferInline = PreferInline;
93047
93200
 
93048
93201
  /***/ },
93049
93202
 
93203
+ /***/ "./node_modules/@abaplint/core/build/src/rules/prefer_insert_into_table.js"
93204
+ /*!*********************************************************************************!*\
93205
+ !*** ./node_modules/@abaplint/core/build/src/rules/prefer_insert_into_table.js ***!
93206
+ \*********************************************************************************/
93207
+ (__unused_webpack_module, exports, __webpack_require__) {
93208
+
93209
+ "use strict";
93210
+
93211
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
93212
+ if (k2 === undefined) k2 = k;
93213
+ var desc = Object.getOwnPropertyDescriptor(m, k);
93214
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
93215
+ desc = { enumerable: true, get: function() { return m[k]; } };
93216
+ }
93217
+ Object.defineProperty(o, k2, desc);
93218
+ }) : (function(o, m, k, k2) {
93219
+ if (k2 === undefined) k2 = k;
93220
+ o[k2] = m[k];
93221
+ }));
93222
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
93223
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
93224
+ }) : function(o, v) {
93225
+ o["default"] = v;
93226
+ });
93227
+ var __importStar = (this && this.__importStar) || (function () {
93228
+ var ownKeys = function(o) {
93229
+ ownKeys = Object.getOwnPropertyNames || function (o) {
93230
+ var ar = [];
93231
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
93232
+ return ar;
93233
+ };
93234
+ return ownKeys(o);
93235
+ };
93236
+ return function (mod) {
93237
+ if (mod && mod.__esModule) return mod;
93238
+ var result = {};
93239
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
93240
+ __setModuleDefault(result, mod);
93241
+ return result;
93242
+ };
93243
+ })();
93244
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
93245
+ exports.PreferInsertIntoTable = exports.PreferInsertIntoTableConf = void 0;
93246
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
93247
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
93248
+ const _abap_rule_1 = __webpack_require__(/*! ./_abap_rule */ "./node_modules/@abaplint/core/build/src/rules/_abap_rule.js");
93249
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
93250
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
93251
+ const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
93252
+ class PreferInsertIntoTableConf extends _basic_rule_config_1.BasicRuleConfig {
93253
+ }
93254
+ exports.PreferInsertIntoTableConf = PreferInsertIntoTableConf;
93255
+ class PreferInsertIntoTable extends _abap_rule_1.ABAPRule {
93256
+ constructor() {
93257
+ super(...arguments);
93258
+ this.conf = new PreferInsertIntoTableConf();
93259
+ }
93260
+ getMetadata() {
93261
+ return {
93262
+ key: "prefer_insert_into_table",
93263
+ title: "Prefer INSERT INTO TABLE over APPEND",
93264
+ shortDescription: `Prefer INSERT INTO TABLE over APPEND`,
93265
+ extendedInformation: `INSERT INTO TABLE respects the table type, while APPEND always adds to the end regardless of table type.
93266
+
93267
+ https://github.com/SAP/styleguides/blob/main/clean-abap/CleanABAP.md`,
93268
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Styleguide, _irule_1.RuleTag.Quickfix],
93269
+ badExample: `APPEND row TO itab.`,
93270
+ goodExample: `INSERT row INTO TABLE itab.`,
93271
+ };
93272
+ }
93273
+ getConfig() { return this.conf; }
93274
+ setConfig(conf) { this.conf = conf; }
93275
+ runParsed(file) {
93276
+ const issues = [];
93277
+ for (const stat of file.getStatements()) {
93278
+ if (!(stat.get() instanceof Statements.Append)) {
93279
+ continue;
93280
+ }
93281
+ if (stat.concatTokens().toUpperCase().includes(" SORTED BY ")) {
93282
+ continue;
93283
+ }
93284
+ const fix = this.buildFix(file, stat);
93285
+ const message = "Prefer INSERT INTO TABLE over APPEND";
93286
+ issues.push(issue_1.Issue.atStatement(file, stat, message, this.getMetadata().key, this.conf.severity, fix));
93287
+ }
93288
+ return issues;
93289
+ }
93290
+ buildFix(file, stat) {
93291
+ const tokens = stat.getTokens();
93292
+ // Find the APPEND token (first token)
93293
+ const appendToken = tokens[0];
93294
+ if (appendToken.getStr().toUpperCase() !== "APPEND") {
93295
+ return undefined;
93296
+ }
93297
+ // Find the last "TO" token that precedes the table target.
93298
+ // In all fixable forms the table target follows the last standalone "TO":
93299
+ // APPEND row TO itab
93300
+ // APPEND LINES OF itab2 TO itab
93301
+ // APPEND LINES OF itab2 FROM 1 TO 3 TO itab <- last TO is the table
93302
+ // APPEND INITIAL LINE TO itab
93303
+ let lastToIndex = -1;
93304
+ for (let i = tokens.length - 1; i >= 0; i--) {
93305
+ if (tokens[i].getStr().toUpperCase() === "TO") {
93306
+ lastToIndex = i;
93307
+ break;
93308
+ }
93309
+ }
93310
+ if (lastToIndex < 0) {
93311
+ return undefined;
93312
+ }
93313
+ const toToken = tokens[lastToIndex];
93314
+ const fix1 = edit_helper_1.EditHelper.replaceToken(file, appendToken, "INSERT");
93315
+ const fix2 = edit_helper_1.EditHelper.replaceToken(file, toToken, "INTO TABLE");
93316
+ return edit_helper_1.EditHelper.merge(fix1, fix2);
93317
+ }
93318
+ }
93319
+ exports.PreferInsertIntoTable = PreferInsertIntoTable;
93320
+ //# sourceMappingURL=prefer_insert_into_table.js.map
93321
+
93322
+ /***/ },
93323
+
93050
93324
  /***/ "./node_modules/@abaplint/core/build/src/rules/prefer_is_not.js"
93051
93325
  /*!**********************************************************************!*\
93052
93326
  !*** ./node_modules/@abaplint/core/build/src/rules/prefer_is_not.js ***!
@@ -100908,6 +101182,178 @@ exports.WhitespaceEnd = WhitespaceEnd;
100908
101182
 
100909
101183
  /***/ },
100910
101184
 
101185
+ /***/ "./node_modules/@abaplint/core/build/src/rules/wrong_abapdoc_position.js"
101186
+ /*!*******************************************************************************!*\
101187
+ !*** ./node_modules/@abaplint/core/build/src/rules/wrong_abapdoc_position.js ***!
101188
+ \*******************************************************************************/
101189
+ (__unused_webpack_module, exports, __webpack_require__) {
101190
+
101191
+ "use strict";
101192
+
101193
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
101194
+ if (k2 === undefined) k2 = k;
101195
+ var desc = Object.getOwnPropertyDescriptor(m, k);
101196
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
101197
+ desc = { enumerable: true, get: function() { return m[k]; } };
101198
+ }
101199
+ Object.defineProperty(o, k2, desc);
101200
+ }) : (function(o, m, k, k2) {
101201
+ if (k2 === undefined) k2 = k;
101202
+ o[k2] = m[k];
101203
+ }));
101204
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
101205
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
101206
+ }) : function(o, v) {
101207
+ o["default"] = v;
101208
+ });
101209
+ var __importStar = (this && this.__importStar) || (function () {
101210
+ var ownKeys = function(o) {
101211
+ ownKeys = Object.getOwnPropertyNames || function (o) {
101212
+ var ar = [];
101213
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
101214
+ return ar;
101215
+ };
101216
+ return ownKeys(o);
101217
+ };
101218
+ return function (mod) {
101219
+ if (mod && mod.__esModule) return mod;
101220
+ var result = {};
101221
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
101222
+ __setModuleDefault(result, mod);
101223
+ return result;
101224
+ };
101225
+ })();
101226
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
101227
+ exports.WrongAbapdocPosition = exports.WrongAbapdocPositionConf = void 0;
101228
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
101229
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
101230
+ const _abap_rule_1 = __webpack_require__(/*! ./_abap_rule */ "./node_modules/@abaplint/core/build/src/rules/_abap_rule.js");
101231
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
101232
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
101233
+ const _statement_1 = __webpack_require__(/*! ../abap/2_statements/statements/_statement */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/_statement.js");
101234
+ class WrongAbapdocPositionConf extends _basic_rule_config_1.BasicRuleConfig {
101235
+ }
101236
+ exports.WrongAbapdocPositionConf = WrongAbapdocPositionConf;
101237
+ class WrongAbapdocPosition extends _abap_rule_1.ABAPRule {
101238
+ constructor() {
101239
+ super(...arguments);
101240
+ this.conf = new WrongAbapdocPositionConf();
101241
+ }
101242
+ getMetadata() {
101243
+ return {
101244
+ key: "wrong_abapdoc_position",
101245
+ title: "Wrong ABAP Doc position",
101246
+ shortDescription: `ABAP Doc must be placed directly in front of the declaration it documents.`,
101247
+ extendedInformation: `ABAP Doc documents the single declaration following it, an ABAP Doc block placed
101248
+ elsewhere is silently ignored, leaving the declaration undocumented.
101249
+
101250
+ The following positions are reported,
101251
+ * in front of a chained keyword, ie. before "CONSTANTS:" instead of after the colon,
101252
+ * in the middle of a statement, ie. between the parameters of a METHODS definition,
101253
+ * directly in front of ENDCLASS, ENDINTERFACE or a SECTION statement.
101254
+
101255
+ Only checks ABAP Doc inside class definitions and interfaces.`,
101256
+ tags: [_irule_1.RuleTag.SingleFile],
101257
+ badExample: `CLASS zcl_foo DEFINITION PUBLIC.
101258
+ PUBLIC SECTION.
101259
+ "! Navigation modes
101260
+ CONSTANTS:
101261
+ BEGIN OF cs_nav_mode,
101262
+ back TYPE i VALUE 1,
101263
+ END OF cs_nav_mode.
101264
+ ENDCLASS.`,
101265
+ goodExample: `CLASS zcl_foo DEFINITION PUBLIC.
101266
+ PUBLIC SECTION.
101267
+ CONSTANTS:
101268
+ "! Navigation modes
101269
+ BEGIN OF cs_nav_mode,
101270
+ back TYPE i VALUE 1,
101271
+ END OF cs_nav_mode.
101272
+ ENDCLASS.`,
101273
+ };
101274
+ }
101275
+ getConfig() {
101276
+ return this.conf;
101277
+ }
101278
+ setConfig(conf) {
101279
+ this.conf = conf;
101280
+ }
101281
+ runParsed(file) {
101282
+ const issues = [];
101283
+ const statements = file.getStatements();
101284
+ let definition = false;
101285
+ for (let i = 0; i < statements.length; i++) {
101286
+ const statement = statements[i];
101287
+ const type = statement.get();
101288
+ if (type instanceof Statements.ClassDefinition || type instanceof Statements.Interface) {
101289
+ definition = true;
101290
+ continue;
101291
+ }
101292
+ else if (type instanceof Statements.EndClass || type instanceof Statements.EndInterface) {
101293
+ definition = false;
101294
+ continue;
101295
+ }
101296
+ else if (definition === false || this.isAbapdoc(statement) === false) {
101297
+ continue;
101298
+ }
101299
+ else if (this.isAbapdoc(statements[i - 1]) === true
101300
+ && statements[i - 1].getStart().getRow() + 1 === statement.getStart().getRow()) {
101301
+ continue; // only report the first row of each block
101302
+ }
101303
+ let next = undefined;
101304
+ for (let j = i + 1; j < statements.length; j++) {
101305
+ if (statements[j].get() instanceof _statement_1.Comment) {
101306
+ continue;
101307
+ }
101308
+ next = statements[j];
101309
+ break;
101310
+ }
101311
+ if (next === undefined) {
101312
+ continue;
101313
+ }
101314
+ const message = this.check(statement, next);
101315
+ if (message !== undefined) {
101316
+ issues.push(issue_1.Issue.atStatement(file, statement, message, this.getMetadata().key, this.conf.severity));
101317
+ }
101318
+ }
101319
+ return issues;
101320
+ }
101321
+ isAbapdoc(statement) {
101322
+ return (statement === null || statement === void 0 ? void 0 : statement.get()) instanceof _statement_1.Comment
101323
+ && statement.getFirstToken().getStr().startsWith(`"!`);
101324
+ }
101325
+ check(abapdoc, next) {
101326
+ const type = next.get();
101327
+ if (type instanceof Statements.EndClass
101328
+ || type instanceof Statements.EndInterface
101329
+ || type instanceof Statements.Public
101330
+ || type instanceof Statements.Protected
101331
+ || type instanceof Statements.Private) {
101332
+ return "ABAP Doc does not document anything, move or delete it";
101333
+ }
101334
+ const position = abapdoc.getStart();
101335
+ const colon = next.getColon();
101336
+ if (colon === undefined) {
101337
+ if (position.isAfter(next.getStart()) === true) {
101338
+ return "ABAP Doc inside statement, move it in front of the statement";
101339
+ }
101340
+ return undefined;
101341
+ }
101342
+ if (position.isBefore(colon.getStart()) === true) {
101343
+ return "ABAP Doc in front of chained statement, move it after the colon";
101344
+ }
101345
+ const member = next.getTokens().find(t => t.getStart().isAfter(colon.getStart()));
101346
+ if (member !== undefined && position.isAfter(member.getStart()) === true) {
101347
+ return "ABAP Doc inside statement, move it in front of the chained declaration";
101348
+ }
101349
+ return undefined;
101350
+ }
101351
+ }
101352
+ exports.WrongAbapdocPosition = WrongAbapdocPosition;
101353
+ //# sourceMappingURL=wrong_abapdoc_position.js.map
101354
+
101355
+ /***/ },
101356
+
100911
101357
  /***/ "./node_modules/@abaplint/core/build/src/rules/xml_bom.js"
100912
101358
  /*!****************************************************************!*\
100913
101359
  !*** ./node_modules/@abaplint/core/build/src/rules/xml_bom.js ***!
@@ -103075,12 +103521,48 @@ exports.DatabaseSetup = DatabaseSetup;
103075
103521
  /*!***************************************************************************!*\
103076
103522
  !*** ./node_modules/@abaplint/transpiler/build/src/db/populate_tables.js ***!
103077
103523
  \***************************************************************************/
103078
- (__unused_webpack_module, exports) {
103524
+ (__unused_webpack_module, exports, __webpack_require__) {
103079
103525
 
103080
103526
  "use strict";
103081
103527
 
103528
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
103529
+ if (k2 === undefined) k2 = k;
103530
+ var desc = Object.getOwnPropertyDescriptor(m, k);
103531
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
103532
+ desc = { enumerable: true, get: function() { return m[k]; } };
103533
+ }
103534
+ Object.defineProperty(o, k2, desc);
103535
+ }) : (function(o, m, k, k2) {
103536
+ if (k2 === undefined) k2 = k;
103537
+ o[k2] = m[k];
103538
+ }));
103539
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
103540
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
103541
+ }) : function(o, v) {
103542
+ o["default"] = v;
103543
+ });
103544
+ var __importStar = (this && this.__importStar) || (function () {
103545
+ var ownKeys = function(o) {
103546
+ ownKeys = Object.getOwnPropertyNames || function (o) {
103547
+ var ar = [];
103548
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
103549
+ return ar;
103550
+ };
103551
+ return ownKeys(o);
103552
+ };
103553
+ return function (mod) {
103554
+ if (mod && mod.__esModule) return mod;
103555
+ var result = {};
103556
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
103557
+ __setModuleDefault(result, mod);
103558
+ return result;
103559
+ };
103560
+ })();
103082
103561
  Object.defineProperty(exports, "__esModule", ({ value: true }));
103083
103562
  exports.PopulateTables = void 0;
103563
+ /* eslint-disable max-len */
103564
+ const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js"));
103565
+ const w3mi_name_1 = __webpack_require__(/*! ../w3mi_name */ "./node_modules/@abaplint/transpiler/build/src/w3mi_name.js");
103084
103566
  class PopulateTables {
103085
103567
  hasREPOSRC;
103086
103568
  hasSEOSUBCO;
@@ -103216,7 +103698,9 @@ class PopulateTables {
103216
103698
  return "";
103217
103699
  }
103218
103700
  const type = obj.getType().toUpperCase();
103219
- const name = obj.getName().toUpperCase();
103701
+ const name = obj instanceof abaplint.Objects.WebMIME
103702
+ ? (0, w3mi_name_1.w3miObjectName)(obj)
103703
+ : obj.getName().toUpperCase();
103220
103704
  return `INSERT INTO "tadir" ("pgmid", "object", "obj_name", "devclass", "korrnum", "srcsystem", "delflag", "genflag", "edtflag", "masterlang")
103221
103705
  VALUES ('R3TR', '${type}', '${this.escape(name)}', '$TMP', '', 'ABC', '', '', '', 'E');`;
103222
103706
  }
@@ -103225,7 +103709,7 @@ class PopulateTables {
103225
103709
  return [];
103226
103710
  }
103227
103711
  const ret = [];
103228
- const name = obj.getName().toUpperCase();
103712
+ const name = (0, w3mi_name_1.w3miObjectName)(obj);
103229
103713
  for (const [key, value] of Object.entries(obj.getParameters())) {
103230
103714
  ret.push(`INSERT INTO "wwwparams" ("relid", "objid", "name", "value") VALUES ('MI', '${name}', '${this.escape(key)}', '${this.escape(value)}');`);
103231
103715
  }
@@ -107087,10 +107571,50 @@ class SourceTranspiler {
107087
107571
  constructor(addGet = false) {
107088
107572
  this.addGet = addGet;
107089
107573
  }
107574
+ // the operands of a chain of & / && (the rearranger leaves them as a binary
107575
+ // tree of Source nodes); an empty list when the node is not such a chain
107576
+ concatOperands(node) {
107577
+ const children = node.getChildren();
107578
+ if (children.length !== 3) {
107579
+ return [];
107580
+ }
107581
+ const op = children[1];
107582
+ if (!(op instanceof core_1.Nodes.TokenNode) || (op.getFirstToken().getStr() !== "&&" && op.getFirstToken().getStr() !== "&")) {
107583
+ return [];
107584
+ }
107585
+ const side = (c) => {
107586
+ if (c instanceof core_1.Nodes.ExpressionNode && c.get() instanceof core_1.Expressions.Source) {
107587
+ const inner = this.concatOperands(c);
107588
+ return inner.length > 0 ? inner : [c];
107589
+ }
107590
+ const wrapped = new core_1.Nodes.ExpressionNode(new core_1.Expressions.Source());
107591
+ wrapped.setChildren([c]);
107592
+ return [wrapped];
107593
+ };
107594
+ return [...side(children[0]), ...side(children[2])];
107595
+ }
107090
107596
  transpile(node, traversal, context) {
107091
107597
  let ret = new chunk_1.Chunk();
107092
107598
  const post = new chunk_1.Chunk();
107093
107599
  const children = node.getChildren();
107600
+ // a & b & c ... : one flat concat([a, b, c]) instead of one nested
107601
+ // concat( ) per operand (a long chain would otherwise exhaust the stack
107602
+ // of a parser with a small budget, e.g. a service worker's)
107603
+ const operands = this.concatOperands(node);
107604
+ if (operands.length > 2) {
107605
+ const flat = new chunk_1.Chunk().appendString("abap.operators.concat([");
107606
+ for (let i = 0; i < operands.length; i++) {
107607
+ if (i > 0) {
107608
+ flat.appendString(",");
107609
+ }
107610
+ flat.appendChunk(new SourceTranspiler(false).transpile(operands[i], traversal, context));
107611
+ }
107612
+ flat.appendString("])");
107613
+ if (this.addGet) {
107614
+ flat.appendString(".get()");
107615
+ }
107616
+ return flat;
107617
+ }
107094
107618
  // Pre-scan for unary prefix +/- tokens (WDashW / WPlusW) before any expression
107095
107619
  let prefixNegated = false;
107096
107620
  let startIdx = 0;
@@ -109670,6 +110194,7 @@ const traversal_1 = __webpack_require__(/*! ../traversal */ "./node_modules/@aba
109670
110194
  const requires_1 = __webpack_require__(/*! ../requires */ "./node_modules/@abaplint/transpiler/build/src/requires.js");
109671
110195
  const rearranger_1 = __webpack_require__(/*! ../rearranger */ "./node_modules/@abaplint/transpiler/build/src/rearranger.js");
109672
110196
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
110197
+ const initialization_1 = __webpack_require__(/*! ../initialization */ "./node_modules/@abaplint/transpiler/build/src/initialization.js");
109673
110198
  class HandleABAP {
109674
110199
  options;
109675
110200
  constructor(options) {
@@ -109774,10 +110299,10 @@ class HandleABAP {
109774
110299
  continue;
109775
110300
  }
109776
110301
  if (name) {
109777
- contents.appendString("const {" + traversal_1.Traversal.escapeNamespace(name) + "} = await import(\"./" + filename.replace(/#/g, "%23") + "\");\n");
110302
+ contents.appendString("const {" + traversal_1.Traversal.escapeNamespace(name) + "} = await import(\"./" + (0, initialization_1.escapeFilenameForImport)(filename) + "\");\n");
109778
110303
  }
109779
110304
  else {
109780
- contents.appendString("await import(\"./" + filename.replace(/#/g, "%23") + "\");\n");
110305
+ contents.appendString("await import(\"./" + (0, initialization_1.escapeFilenameForImport)(filename) + "\");\n");
109781
110306
  }
109782
110307
  }
109783
110308
  contents.appendChunk(output.chunk);
@@ -110388,6 +110913,7 @@ exports.HandleView = HandleView;
110388
110913
  Object.defineProperty(exports, "__esModule", ({ value: true }));
110389
110914
  exports.HandleW3MI = void 0;
110390
110915
  const chunk_1 = __webpack_require__(/*! ../chunk */ "./node_modules/@abaplint/transpiler/build/src/chunk.js");
110916
+ const w3mi_name_1 = __webpack_require__(/*! ../w3mi_name */ "./node_modules/@abaplint/transpiler/build/src/w3mi_name.js");
110391
110917
  class HandleW3MI {
110392
110918
  runObject(obj, _reg) {
110393
110919
  const filename = obj.getXMLFile()?.getFilename().replace(".xml", ".mjs").toLowerCase();
@@ -110396,7 +110922,7 @@ class HandleW3MI {
110396
110922
  }
110397
110923
  obj.parse();
110398
110924
  const dataFile = obj.getDataFile();
110399
- const chunk = new chunk_1.Chunk().appendString(`abap.W3MI["${obj.getName().toUpperCase()}"] = {
110925
+ const chunk = new chunk_1.Chunk().appendString(`abap.W3MI["${(0, w3mi_name_1.w3miObjectName)(obj)}"] = {
110400
110926
  "objectType": "W3MI",
110401
110927
  "filename": ${JSON.stringify(dataFile?.getFilename())},
110402
110928
  };`);
@@ -110646,12 +111172,24 @@ var __importStar = (this && this.__importStar) || (function () {
110646
111172
  Object.defineProperty(exports, "__esModule", ({ value: true }));
110647
111173
  exports.Initialization = void 0;
110648
111174
  exports.escapeNamespaceFilename = escapeNamespaceFilename;
111175
+ exports.escapeFilenameForImport = escapeFilenameForImport;
110649
111176
  const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js"));
110650
111177
  const handle_fugr_1 = __webpack_require__(/*! ./handlers/handle_fugr */ "./node_modules/@abaplint/transpiler/build/src/handlers/handle_fugr.js");
110651
111178
  function escapeNamespaceFilename(filename) {
110652
111179
  // ES modules are resolved and cached as URLs. This means that special characters must be
110653
111180
  // percent-encoded, such as # with %23 and ? with %3F.
110654
- return filename.replace(/\//g, "%23");
111181
+ //
111182
+ // The percent sign goes first, and the order is the whole point. A URL is
111183
+ // percent-decoded before it is resolved, so a file whose *name* contains a
111184
+ // percent has to carry it escaped or the decoding eats it: abapGit stores
111185
+ // ZO4D_06_PLASMA.PNG as zo4d_06_plasma%2epng, and "./zo4d_06_plasma%2epng.w3mi.mjs"
111186
+ // resolves to zo4d_06_plasma.png.w3mi.mjs, which is not there. Escaping the
111187
+ // percent after the slash would corrupt the %23 this function just wrote.
111188
+ return filename.replace(/%/g, "%25").replace(/\//g, "%23");
111189
+ }
111190
+ // The same rule for a name that already carries abapGit's # rather than a slash
111191
+ function escapeFilenameForImport(filename) {
111192
+ return filename.replace(/%/g, "%25").replace(/#/g, "%23");
110655
111193
  }
110656
111194
  class Initialization {
110657
111195
  script(reg, dbSetup, options, useImport, pluginOutputs) {
@@ -110895,9 +111433,13 @@ exports.DEFAULT_KEYWORDS = new Set([
110895
111433
  "throw", "throws", "transient", "true",
110896
111434
  "try", "typeof", "var", "void",
110897
111435
  "delete",
110898
- "volatile", "while", "yield"
111436
+ "volatile", "while", "yield",
111437
+ // both produce a SyntaxError in the emitted module: they are reserved words
111438
+ // in JavaScript and legal ABAP names. "super" is NOT in this list on purpose -
111439
+ // the emitted code uses the JS `super` for ABAP's `super->method( )`, and
111440
+ // escaping it turns that call into `$super.get().method()`
111441
+ "case", "with"
110899
111442
  ]);
110900
- // "with"
110901
111443
  //# sourceMappingURL=keywords.js.map
110902
111444
 
110903
111445
  /***/ },
@@ -111100,7 +111642,23 @@ class Rearranger {
111100
111642
  return node;
111101
111643
  }
111102
111644
  const withoutLast = node.getChildren().slice(0, children.length - 1);
111103
- const flat = withoutLast.concat(last.getChildren());
111645
+ // The nested children come up so that precedence can be decided across
111646
+ // the whole chain, and everything before the nested operator has to
111647
+ // arrive as one operand. For "a * b / c" that is already true, b being a
111648
+ // single Source. For "a * CONV f( b ) / c" it is not: CONV is five
111649
+ // children, and hoisting them one by one puts the type name and the
111650
+ // body beside the arithmetic, where the transpiler reads them as
111651
+ // operands and loses the rest of the expression. So the head is wrapped
111652
+ // back into one Source before it moves.
111653
+ const nested = last.getChildren();
111654
+ const firstOperator = nested.findIndex((c) => c instanceof core_1.Nodes.ExpressionNode && c.get() instanceof core_1.Expressions.ArithOperator);
111655
+ let head = nested.slice(0, firstOperator);
111656
+ if (head.length > 1) {
111657
+ const operand = new core_1.Nodes.ExpressionNode(last.get());
111658
+ operand.setChildren(head);
111659
+ head = [operand];
111660
+ }
111661
+ const flat = withoutLast.concat(head, nested.slice(firstOperator));
111104
111662
  node.setChildren(flat);
111105
111663
  return node;
111106
111664
  }
@@ -113510,11 +114068,39 @@ class CreateObjectTranspiler {
113510
114068
  ret += ` ${id} = abap.Classes[${internalName}.get().trimEnd()];\n`;
113511
114069
  ret += `}\n`;
113512
114070
  ret += `if (${id} === undefined) { throw new ${cx}; }\n`;
114071
+ // ...and it has to be compatible with the STATIC type of the target.
114072
+ // A system raises CX_SY_CREATE_OBJECT_ERROR for a class that is not,
114073
+ // before the constructor runs; without this the class was assigned to
114074
+ // the reference whatever it was, and the mismatch surfaced later as a
114075
+ // javascript TypeError on the first member access - which no CATCH
114076
+ // takes, so a caller that guards its dynamic CREATE OBJECT the way the
114077
+ // documentation asks for was not guarded at all
114078
+ const staticName = this.findTargetTypeName(node, traversal);
114079
+ if (staticName !== undefined) {
114080
+ const staticType = traversal.lookupClassOrInterface(staticName, node.getFirstToken());
114081
+ ret += `abap.statements.checkCreateObjectType(${id}, ${staticType}, "${staticName.toUpperCase()}");\n`;
114082
+ }
113513
114083
  clas = id;
113514
114084
  }
113515
114085
  ret += target + ".set(await (new " + clas + "()).constructor_(" + para + "));";
113516
114086
  return new chunk_1.Chunk(ret);
113517
114087
  }
114088
+ /** The name of the target reference's STATIC type, or undefined when there
114089
+ * is nothing to check against - a generic `REF TO object`, or a target this
114090
+ * cannot resolve (which is reported by findClassName on the static path and
114091
+ * must not turn a working dynamic CREATE OBJECT into a transpile error
114092
+ * here). */
114093
+ findTargetTypeName(node, traversal) {
114094
+ const scope = traversal.findCurrentScopeByToken(node.getFirstToken());
114095
+ if (scope === undefined) {
114096
+ return undefined;
114097
+ }
114098
+ const type = traversal.determineType(node, scope);
114099
+ if (type instanceof abaplint.BasicTypes.ObjectReferenceType) {
114100
+ return type.getIdentifierName();
114101
+ }
114102
+ return undefined;
114103
+ }
113518
114104
  findClassName(node, traversal) {
113519
114105
  const c = node.findDirectExpression(abaplint.Expressions.ClassName);
113520
114106
  if (c) {
@@ -122047,16 +122633,16 @@ class FunctionModuleTranspiler {
122047
122633
  direction = "importing";
122048
122634
  }
122049
122635
  // note: all directions are optional
122050
- let name = p.name.toLowerCase();
122051
- if (traversal.options?.keywords?.some(a => a === name)) {
122052
- name += "_";
122053
- }
122054
- ret += `let ${name} = INPUT.${direction}?.${name};\n`;
122636
+ const name = p.name.toLowerCase();
122637
+ // a parameter named like a JavaScript keyword, RETURN on every BAPI,
122638
+ // is declared the way the function body refers to it
122639
+ const variable = traversal_1.Traversal.prefixVariable(name);
122640
+ ret += `let ${variable} = INPUT.${direction}?.${name};\n`;
122055
122641
  const type = scope?.findVariable(name)?.getType();
122056
122642
  if (type !== undefined && p.optional === true) {
122057
122643
  // todo, set DEFAULT value
122058
- ret += `if (${name} === undefined) {
122059
- ${name} = ${transpile_types_1.TranspileTypes.toType(type)};
122644
+ ret += `if (${variable} === undefined) {
122645
+ ${variable} = ${transpile_types_1.TranspileTypes.toType(type)};
122060
122646
  }\n`;
122061
122647
  }
122062
122648
  }
@@ -124717,6 +125303,58 @@ exports.Validation = Validation;
124717
125303
 
124718
125304
  /***/ },
124719
125305
 
125306
+ /***/ "./node_modules/@abaplint/transpiler/build/src/w3mi_name.js"
125307
+ /*!******************************************************************!*\
125308
+ !*** ./node_modules/@abaplint/transpiler/build/src/w3mi_name.js ***!
125309
+ \******************************************************************/
125310
+ (__unused_webpack_module, exports) {
125311
+
125312
+ "use strict";
125313
+
125314
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
125315
+ exports.w3miObjectName = w3miObjectName;
125316
+ /**
125317
+ * The name a system knows a Web Repository object by: `wwwdata-objid`.
125318
+ *
125319
+ * It is not the file name. abapGit percent-escapes the characters a file name
125320
+ * cannot hold, so `ZOISEE-EAR-02.MP3` is stored as `zoisee-ear-02%2emp3`, and
125321
+ * abaplint derives an object's name from the file it was read from. Keying
125322
+ * anything a caller sees on that spelling turns an abapGit storage detail into
125323
+ * an API: ABAP that asks `SELECT ... FROM wwwparams WHERE objid = 'X.MP3'`,
125324
+ * which is how the object is addressed on a system, then finds nothing.
125325
+ *
125326
+ * The real name is in the object's own XML, and abapGit puts it there from the
125327
+ * key itself — `io_xml->add( iv_name = 'NAME' ig_data = ms_key-objid )` in
125328
+ * `zcl_abapgit_object_w3xx_super`, in both serializer versions.
125329
+ */
125330
+ function w3miObjectName(obj) {
125331
+ const raw = obj.getXMLFile()?.getRaw();
125332
+ if (raw !== undefined) {
125333
+ // every entry of <PARAMS> carries a <NAME> of its own, so only the part
125334
+ // before the parameter list can be searched for the object's
125335
+ const head = raw.split(/<PARAMS[\s/>]/)[0];
125336
+ const found = /<NAME>([^<]*)<\/NAME>/.exec(head);
125337
+ const name = found === null ? "" : unescapeXML(found[1]).trim();
125338
+ if (name.length > 0) {
125339
+ return name.toUpperCase();
125340
+ }
125341
+ }
125342
+ // an object with no XML, or an XML from something that is not abapGit: the
125343
+ // file name is the only name there is, and a wrong key beats no key
125344
+ return obj.getName().toUpperCase();
125345
+ }
125346
+ function unescapeXML(value) {
125347
+ // a W3MI name may hold < and > and &, which reach the file escaped
125348
+ return value.replace(/&lt;/g, "<")
125349
+ .replace(/&gt;/g, ">")
125350
+ .replace(/&quot;/g, "\"")
125351
+ .replace(/&apos;/g, "'")
125352
+ .replace(/&amp;/g, "&");
125353
+ }
125354
+ //# sourceMappingURL=w3mi_name.js.map
125355
+
125356
+ /***/ },
125357
+
124720
125358
  /***/ "./node_modules/progress/index.js"
124721
125359
  /*!****************************************!*\
124722
125360
  !*** ./node_modules/progress/index.js ***!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler-cli",
3
- "version": "2.13.86",
3
+ "version": "2.13.88",
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.50",
31
- "@abaplint/transpiler": "^2.13.86",
30
+ "@abaplint/core": "^2.120.54",
31
+ "@abaplint/transpiler": "^2.13.88",
32
32
  "@types/glob": "^9.0.0",
33
33
  "@types/node": "^26.4.1",
34
34
  "@types/progress": "^2.0.7",