@abaplint/transpiler-cli 2.14.12 → 2.14.14

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/README.md CHANGED
@@ -98,6 +98,29 @@ The transpiler library retains flat output unless its caller supplies an output
98
98
  folder map as the third argument to `Transpiler.run()`. This layout is enabled
99
99
  automatically by the CLI.
100
100
 
101
+ ## Pinning a library
102
+
103
+ A library given by `url` is cloned when the CLI runs, so without more the
104
+ default branch of that moment is used and the same project commit can build
105
+ differently from one day to the next. Set `libs[].ref` to a commit SHA, a tag
106
+ or a branch to make the build reproducible:
107
+
108
+ ```json
109
+ {
110
+ "libs": [
111
+ {"url": "https://github.com/open-abap/open-abap-core", "ref": "239b817e6fd05e62854fed25663f002c60dda07c"},
112
+ {"url": "https://github.com/example/utilities", "ref": "v1.2.0"}
113
+ ]
114
+ }
115
+ ```
116
+
117
+ A branch or tag is cloned shallow. A commit is fetched shallow by name; when
118
+ the server does not serve that, or the SHA is abbreviated, the full history is
119
+ fetched and the commit checked out from it. The CLI prints the commit of every
120
+ library it clones, and of every `folder` library that is a git checkout, as
121
+ `commit: <sha>` next to the file count, so the commits of a build can be
122
+ recorded.
123
+
101
124
  ## Duplicate dependencies
102
125
 
103
126
  By default, two libraries defining the same ABAP object (type and name) cause
package/build/bundle.js CHANGED
@@ -257,14 +257,101 @@ exports.FileOperations = FileOperations;
257
257
  /*!****************************!*\
258
258
  !*** ./build/git_clone.js ***!
259
259
  \****************************/
260
- (__unused_webpack_module, exports) {
260
+ (__unused_webpack_module, exports, __webpack_require__) {
261
261
 
262
262
  "use strict";
263
263
 
264
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
265
+ if (k2 === undefined) k2 = k;
266
+ var desc = Object.getOwnPropertyDescriptor(m, k);
267
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
268
+ desc = { enumerable: true, get: function() { return m[k]; } };
269
+ }
270
+ Object.defineProperty(o, k2, desc);
271
+ }) : (function(o, m, k, k2) {
272
+ if (k2 === undefined) k2 = k;
273
+ o[k2] = m[k];
274
+ }));
275
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
276
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
277
+ }) : function(o, v) {
278
+ o["default"] = v;
279
+ });
280
+ var __importStar = (this && this.__importStar) || (function () {
281
+ var ownKeys = function(o) {
282
+ ownKeys = Object.getOwnPropertyNames || function (o) {
283
+ var ar = [];
284
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
285
+ return ar;
286
+ };
287
+ return ownKeys(o);
288
+ };
289
+ return function (mod) {
290
+ if (mod && mod.__esModule) return mod;
291
+ var result = {};
292
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
293
+ __setModuleDefault(result, mod);
294
+ return result;
295
+ };
296
+ })();
264
297
  Object.defineProperty(exports, "__esModule", ({ value: true }));
298
+ exports.isCommitSha = isCommitSha;
265
299
  exports.buildGitCloneArguments = buildGitCloneArguments;
266
- function buildGitCloneArguments(url) {
267
- return ["clone", "--quiet", "--depth", "1", "--", url, "."];
300
+ exports.cloneRepository = cloneRepository;
301
+ exports.resolveHead = resolveHead;
302
+ const childProcess = __importStar(__webpack_require__(/*! child_process */ "child_process"));
303
+ /** a full or abbreviated commit SHA, as opposed to a branch or tag name */
304
+ function isCommitSha(ref) {
305
+ return /^[0-9a-f]{7,40}$/i.test(ref);
306
+ }
307
+ /** shallow clone of the default branch, or of the branch or tag `ref` */
308
+ function buildGitCloneArguments(url, ref) {
309
+ const branch = ref === undefined || ref === "" ? [] : ["--branch", ref];
310
+ return ["clone", "--quiet", "--depth", "1", ...branch, "--", url, "."];
311
+ }
312
+ function git(args, cwd, stderr = "inherit") {
313
+ childProcess.execFileSync("git", args, { cwd, stdio: ["ignore", "inherit", stderr] });
314
+ }
315
+ /** Clones `url` into the empty directory `dir` and returns the commit checked out.
316
+ *
317
+ * Without `ref` this is a shallow clone of the default branch. A branch or tag
318
+ * is cloned shallow with --branch. A commit SHA is fetched shallow by name,
319
+ * which GitHub and every server with uploadpack.allowReachableSHA1InWant
320
+ * serve; when the server refuses that, or the SHA is abbreviated, the full
321
+ * history is fetched and the commit checked out from it. */
322
+ function cloneRepository(url, dir, ref) {
323
+ if (ref === undefined || ref === "") {
324
+ git(buildGitCloneArguments(url), dir);
325
+ }
326
+ else if (ref.startsWith("-")) {
327
+ throw new Error("Invalid lib ref: " + JSON.stringify(ref));
328
+ }
329
+ else if (isCommitSha(ref)) {
330
+ git(["init", "--quiet"], dir);
331
+ git(["remote", "add", "origin", "--", url], dir);
332
+ try {
333
+ git(["fetch", "--quiet", "--depth", "1", "origin", ref], dir, "pipe");
334
+ git(["checkout", "--quiet", "--detach", "FETCH_HEAD"], dir);
335
+ }
336
+ catch {
337
+ console.log("\tshallow fetch of " + ref + " not served, fetching the full history");
338
+ git(["fetch", "--quiet", "--tags", "origin"], dir);
339
+ git(["checkout", "--quiet", "--detach", ref], dir);
340
+ }
341
+ }
342
+ else {
343
+ git(buildGitCloneArguments(url, ref), dir);
344
+ }
345
+ return resolveHead(dir);
346
+ }
347
+ /** the commit checked out in `dir`, undefined when it is not a git checkout */
348
+ function resolveHead(dir) {
349
+ try {
350
+ return childProcess.execFileSync("git", ["rev-parse", "HEAD"], { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
351
+ }
352
+ catch {
353
+ return undefined;
354
+ }
268
355
  }
269
356
  //# sourceMappingURL=git_clone.js.map
270
357
 
@@ -485,7 +572,6 @@ exports.loadLibraries = loadLibraries;
485
572
  exports.libraryRegistry = libraryRegistry;
486
573
  const fs = __importStar(__webpack_require__(/*! fs */ "fs"));
487
574
  const path = __importStar(__webpack_require__(/*! path */ "path"));
488
- const childProcess = __importStar(__webpack_require__(/*! child_process */ "child_process"));
489
575
  const os = __importStar(__webpack_require__(/*! os */ "os"));
490
576
  const abaplint = __importStar(__webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js"));
491
577
  const file_operations_1 = __webpack_require__(/*! ./file_operations */ "./build/file_operations.js");
@@ -533,13 +619,14 @@ async function loadLibraries(config) {
533
619
  ? "Library must define a non-empty url or an existing folder"
534
620
  : "Library folder not found: " + folder);
535
621
  }
536
- console.log("Clone: " + lib.url);
622
+ console.log("Clone: " + lib.url + (lib.ref ? " at " + lib.ref : ""));
537
623
  dir = fs.mkdtempSync(path.join(os.tmpdir(), "abap_transpile-"));
538
624
  cleanupFolder = true;
539
625
  }
540
626
  try {
541
- if (cleanupFolder) {
542
- childProcess.execFileSync("git", (0, git_clone_1.buildGitCloneArguments)(lib.url), { cwd: dir, stdio: "inherit" });
627
+ const commit = cleanupFolder ? (0, git_clone_1.cloneRepository)(lib.url, dir, lib.ref) : (0, git_clone_1.resolveHead)(dir);
628
+ if (commit !== undefined) {
629
+ console.log("\tcommit: " + commit);
543
630
  }
544
631
  const patterns = typeof lib.files === "string" && lib.files !== "" ? [lib.files]
545
632
  : Array.isArray(lib.files) ? lib.files : ["/src/**"];
@@ -6282,10 +6369,15 @@ class FormParamType extends combi_1.Expression {
6282
6369
  getRunnable() {
6283
6370
  const def = (0, combi_1.seq)("DEFAULT", (0, combi_1.alt)(_1.Constant, _1.FieldChain));
6284
6371
  const table = (0, combi_1.seq)((0, combi_1.alt)("STANDARD", "HASHED", "INDEX", "SORTED", "ANY"), "TABLE");
6285
- const tabseq = (0, combi_1.seq)(table, (0, combi_1.optPrio)((0, combi_1.seq)("OF", _1.TypeName)));
6286
- const ret = (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.TypeName, (0, combi_1.opt)(def));
6287
- const like = (0, combi_1.seq)("LIKE", (0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.FieldChain);
6288
- return (0, combi_1.alt)((0, combi_1.seq)("TYPE", (0, combi_1.altPrio)(tabseq, (0, combi_1.ver)(version_1.Release.v916, (0, combi_1.seq)("ANY", "STRUCTURE")), ret)), like);
6372
+ // the following are not valid FORM typings, fail instead of reading the
6373
+ // remaining words as additional parameters
6374
+ const inline = (0, combi_1.seq)((0, combi_1.alt)("TABLE", "RANGE"), "OF", (0, combi_1.failCombinator)());
6375
+ const tableKey = (0, combi_1.seq)("WITH", (0, combi_1.failCombinator)());
6376
+ const lengthDecimals = (0, combi_1.seq)((0, combi_1.alt)("LENGTH", "DECIMALS"), _1.Integer, (0, combi_1.failCombinator)());
6377
+ const tabseq = (0, combi_1.seq)(table, (0, combi_1.optPrio)((0, combi_1.seq)("OF", (0, combi_1.altPrio)((0, combi_1.seq)("REF TO", (0, combi_1.failCombinator)()), _1.TypeName))), (0, combi_1.optPrio)(tableKey));
6378
+ const ret = (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.TypeName, (0, combi_1.optPrio)(lengthDecimals), (0, combi_1.opt)(def));
6379
+ const like = (0, combi_1.seq)("LIKE", (0, combi_1.altPrio)(inline, (0, combi_1.seq)(table, (0, combi_1.failCombinator)()), (0, combi_1.seq)((0, combi_1.optPrio)((0, combi_1.alt)("REF TO", "LINE OF")), _1.FieldChain)));
6380
+ return (0, combi_1.alt)((0, combi_1.seq)("TYPE", (0, combi_1.altPrio)(tabseq, (0, combi_1.ver)(version_1.Release.v916, (0, combi_1.seq)("ANY", "STRUCTURE")), inline, ret)), like);
6289
6381
  }
6290
6382
  }
6291
6383
  exports.FormParamType = FormParamType;
@@ -29558,6 +29650,15 @@ class TypeUtils {
29558
29650
  }
29559
29651
  return this.isAssignable(source, target);
29560
29652
  }
29653
+ // character-like arguments of built-in functions must be elementary, flat character-like structures are not allowed
29654
+ isAssignableBuiltin(source, target, node) {
29655
+ if (target instanceof basic_1.CLikeType
29656
+ && (source instanceof basic_1.StructureType
29657
+ || (source instanceof basic_1.TableType && source.isWithHeader() && source.getRowType() instanceof basic_1.StructureType))) {
29658
+ return false;
29659
+ }
29660
+ return this.isAssignableStrict(source, target, node);
29661
+ }
29561
29662
  // Typing with the classic STRUCTURE addition, ie. "FORM foo USING bar STRUCTURE zkey",
29562
29663
  // is not a compatibility check: ABAP only checks the fragment view up to the end of the
29563
29664
  // formal structure, so the actual parameter is allowed to be longer. For TABLES parameters
@@ -33190,6 +33291,7 @@ class FieldChain {
33190
33291
  const children = node.getChildren();
33191
33292
  context = this.findTop(children[0], input, refType);
33192
33293
  let prev = children[0].concatTokens();
33294
+ let afterTableExpression = false;
33193
33295
  for (let i = 1; i < children.length; i++) {
33194
33296
  const current = children[i];
33195
33297
  if (current === undefined) {
@@ -33237,6 +33339,9 @@ class FieldChain {
33237
33339
  }
33238
33340
  }
33239
33341
  else if (current.get() instanceof tokens_1.InstanceArrow) {
33342
+ if (afterTableExpression && table_expression_1.TableExpression.checkArrowAfter(input, current.getFirstToken())) {
33343
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
33344
+ }
33240
33345
  if (!(context instanceof basic_1.ObjectReferenceType)
33241
33346
  && !(context instanceof basic_1.DataReference)
33242
33347
  && !(context instanceof basic_1.VoidType)) {
@@ -33269,6 +33374,7 @@ class FieldChain {
33269
33374
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
33270
33375
  }
33271
33376
  table_expression_1.TableExpression.runSyntax(current, input, context);
33377
+ afterTableExpression = true;
33272
33378
  if (!(context instanceof basic_1.VoidType) && !(context instanceof basic_1.UnknownType)) {
33273
33379
  context = context.getRowType();
33274
33380
  }
@@ -34624,6 +34730,7 @@ const method_call_param_1 = __webpack_require__(/*! ./method_call_param */ "./no
34624
34730
  const _reference_1 = __webpack_require__(/*! ../_reference */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_reference.js");
34625
34731
  const component_name_1 = __webpack_require__(/*! ./component_name */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/component_name.js");
34626
34732
  const attribute_name_1 = __webpack_require__(/*! ./attribute_name */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/attribute_name.js");
34733
+ const table_expression_1 = __webpack_require__(/*! ./table_expression */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/table_expression.js");
34627
34734
  const _syntax_input_1 = __webpack_require__(/*! ../_syntax_input */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_syntax_input.js");
34628
34735
  class MethodCallChain {
34629
34736
  static runSyntax(node, input, targetType) {
@@ -34636,6 +34743,13 @@ class MethodCallChain {
34636
34743
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
34637
34744
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
34638
34745
  }
34746
+ // the field chain is followed by "->", eg. "itab[ 1 ]-ref->meth( )"
34747
+ if (first instanceof nodes_1.ExpressionNode
34748
+ && first.get() instanceof Expressions.FieldChain
34749
+ && first.findDirectExpression(Expressions.TableExpression) !== undefined
34750
+ && table_expression_1.TableExpression.checkArrowAfter(input, children[1].getFirstToken())) {
34751
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
34752
+ }
34639
34753
  let currentIndex = 1;
34640
34754
  let context = this.findTop(first, input, targetType);
34641
34755
  if (first.get() instanceof Expressions.MethodCall) {
@@ -34847,6 +34961,7 @@ const _syntax_input_1 = __webpack_require__(/*! ../_syntax_input */ "./node_modu
34847
34961
  const constant_1 = __webpack_require__(/*! ./constant */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/constant.js");
34848
34962
  const _check_offset_length_1 = __webpack_require__(/*! ./_check_offset_length */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_offset_length.js");
34849
34963
  const _check_text_symbol_1 = __webpack_require__(/*! ./_check_text_symbol */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_text_symbol.js");
34964
+ const _builtin_1 = __webpack_require__(/*! ../_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
34850
34965
  class MethodCallParam {
34851
34966
  /** returns the type of the unnamed argument, if any */
34852
34967
  static runSyntax(node, input, method) {
@@ -34923,7 +35038,7 @@ class MethodCallParam {
34923
35038
  else if ((0, _check_text_symbol_1.checkTextSymbol)(child, targetType, input)) {
34924
35039
  return;
34925
35040
  }
34926
- else if (new _type_utils_1.TypeUtils(input.scope).isAssignableStrict(sourceType, targetType, child) === false) {
35041
+ else if (this.isAssignable(sourceType, targetType, child, method, input) === false) {
34927
35042
  const message = "Method parameter type not compatible";
34928
35043
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
34929
35044
  return;
@@ -34943,6 +35058,12 @@ class MethodCallParam {
34943
35058
  }
34944
35059
  return undefined;
34945
35060
  }
35061
+ static isAssignable(sourceType, targetType, child, method, input) {
35062
+ const typeUtils = new _type_utils_1.TypeUtils(input.scope);
35063
+ return method instanceof _builtin_1.BuiltInMethod
35064
+ ? typeUtils.isAssignableBuiltin(sourceType, targetType, child)
35065
+ : typeUtils.isAssignableStrict(sourceType, targetType, child);
35066
+ }
34946
35067
  }
34947
35068
  exports.MethodCallParam = MethodCallParam;
34948
35069
  //# sourceMappingURL=method_call_param.js.map
@@ -35180,6 +35301,7 @@ const field_chain_1 = __webpack_require__(/*! ./field_chain */ "./node_modules/@
35180
35301
  const _reference_1 = __webpack_require__(/*! ../_reference */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_reference.js");
35181
35302
  const _check_offset_length_1 = __webpack_require__(/*! ./_check_offset_length */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_offset_length.js");
35182
35303
  const _check_text_symbol_1 = __webpack_require__(/*! ./_check_text_symbol */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/_check_text_symbol.js");
35304
+ const _builtin_1 = __webpack_require__(/*! ../_builtin */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_builtin.js");
35183
35305
  function normalizeParameterName(name) {
35184
35306
  return name.replace(/^!/, "").toUpperCase();
35185
35307
  }
@@ -35327,6 +35449,10 @@ class MethodParameters {
35327
35449
  if (this.requiredParameters === undefined) {
35328
35450
  this.requiredParameters = new Set(method.getParameters().getRequiredParameters().map(i => i.getName().toUpperCase()));
35329
35451
  }
35452
+ const typeUtils = new _type_utils_1.TypeUtils(input.scope);
35453
+ const assignable = method instanceof _builtin_1.BuiltInMethod
35454
+ ? typeUtils.isAssignableBuiltin.bind(typeUtils)
35455
+ : typeUtils.isAssignableStrict.bind(typeUtils);
35330
35456
  for (const item of items) {
35331
35457
  const parameter = allImporting.find(p => p.getName().toUpperCase() === item.name);
35332
35458
  if (parameter === undefined) {
@@ -35337,7 +35463,7 @@ class MethodParameters {
35337
35463
  else if ((0, _check_text_symbol_1.checkTextSymbol)(item.source, parameter.getType(), input)) {
35338
35464
  return;
35339
35465
  }
35340
- else if (new _type_utils_1.TypeUtils(input.scope).isAssignableStrict(item.sourceType, parameter.getType(), item.source) === false) {
35466
+ else if (assignable(item.sourceType, parameter.getType(), item.source) === false) {
35341
35467
  const message = "Method parameter type not compatible, " + item.name;
35342
35468
  input.issues.push((0, _syntax_input_1.syntaxIssue)(input, node.getFirstToken(), message));
35343
35469
  return;
@@ -35770,6 +35896,8 @@ class NewObject {
35770
35896
  }
35771
35897
  }
35772
35898
  else if (typeName === "#" && targetType) {
35899
+ const tid = new _typed_identifier_1.TypedIdentifier(typeToken, input.filename, targetType);
35900
+ input.scope.addReference(typeToken, tid, _reference_1.ReferenceType.InferredType, input.filename);
35773
35901
  ret = targetType;
35774
35902
  }
35775
35903
  else if (typeName === "#") {
@@ -38033,9 +38161,23 @@ exports.TableExpression = void 0;
38033
38161
  const nodes_1 = __webpack_require__(/*! ../../nodes */ "./node_modules/@abaplint/core/build/src/abap/nodes/index.js");
38034
38162
  const Expressions = __importStar(__webpack_require__(/*! ../../2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
38035
38163
  const source_1 = __webpack_require__(/*! ./source */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/source.js");
38164
+ const _syntax_input_1 = __webpack_require__(/*! ../_syntax_input */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/_syntax_input.js");
38036
38165
  const basic_1 = __webpack_require__(/*! ../../types/basic */ "./node_modules/@abaplint/core/build/src/abap/types/basic/index.js");
38037
38166
  const component_chain_1 = __webpack_require__(/*! ./component_chain */ "./node_modules/@abaplint/core/build/src/abap/5_syntax/expressions/component_chain.js");
38167
+ const version_1 = __webpack_require__(/*! ../../../version */ "./node_modules/@abaplint/core/build/src/version.js");
38038
38168
  class TableExpression {
38169
+ /** below v750 the object component selector "->" cannot follow a table expression, eg. "itab[ 1 ]-ref->meth( )",
38170
+ * returns true and adds an issue if not allowed */
38171
+ static checkArrowAfter(input, arrow) {
38172
+ if ((0, version_1.releaseAtLeast)(input.scope.getRelease(), version_1.Release.v750)
38173
+ || input.scope.getLanguageVersion() === version_1.LanguageVersion.Cloud
38174
+ || input.scope.getOpenABAP()) {
38175
+ return false;
38176
+ }
38177
+ const message = "Object component selector \"->\" after table expression not possible below v750";
38178
+ input.issues.push((0, _syntax_input_1.syntaxIssue)(input, arrow, message));
38179
+ return true;
38180
+ }
38039
38181
  static runSyntax(node, input, rowType) {
38040
38182
  if (node === undefined) {
38041
38183
  return;
@@ -38152,6 +38294,7 @@ class Target {
38152
38294
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
38153
38295
  }
38154
38296
  let currentIndex = 1;
38297
+ let afterTableExpression = false;
38155
38298
  while (currentIndex <= children.length) {
38156
38299
  const current = children[currentIndex];
38157
38300
  if (current === undefined) {
@@ -38177,6 +38320,9 @@ class Target {
38177
38320
  }
38178
38321
  }
38179
38322
  else if (current.get() instanceof tokens_1.InstanceArrow) {
38323
+ if (afterTableExpression && table_expression_1.TableExpression.checkArrowAfter(input, current.getFirstToken())) {
38324
+ return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
38325
+ }
38180
38326
  if (!(context instanceof basic_1.ObjectReferenceType)
38181
38327
  && !(context instanceof basic_1.DataReference)
38182
38328
  && !(context instanceof basic_1.VoidType)) {
@@ -38225,6 +38371,7 @@ class Target {
38225
38371
  return basic_1.VoidType.get(_syntax_input_1.CheckSyntaxKey);
38226
38372
  }
38227
38373
  table_expression_1.TableExpression.runSyntax(current, input, context);
38374
+ afterTableExpression = true;
38228
38375
  if (!(context instanceof basic_1.VoidType) && !(context instanceof unknown_type_1.UnknownType)) {
38229
38376
  context = context.getRowType();
38230
38377
  }
@@ -57740,7 +57887,7 @@ const basic_1 = __webpack_require__(/*! ../abap/types/basic */ "./node_modules/@
57740
57887
  const ddic_1 = __webpack_require__(/*! ../ddic */ "./node_modules/@abaplint/core/build/src/ddic.js");
57741
57888
  class CDSDetermineTypes {
57742
57889
  parseType(reg, parsedData, ddlsName) {
57743
- var _a;
57890
+ var _a, _b;
57744
57891
  const ddic = new ddic_1.DDIC(reg);
57745
57892
  if ((parsedData === null || parsedData === void 0 ? void 0 : parsedData.fields.length) === 0) {
57746
57893
  return basic_1.VoidType.get("DDLS:todo");
@@ -57819,13 +57966,29 @@ class CDSDetermineTypes {
57819
57966
  else {
57820
57967
  components.push({
57821
57968
  name: f.name,
57822
- type: basic_1.VoidType.get("DDLS:fieldname:" + ddlsName),
57969
+ type: (_b = this.findInSources(ddic, parsedData, f.nameInSource)) !== null && _b !== void 0 ? _b : basic_1.VoidType.get("DDLS:fieldname:" + ddlsName),
57823
57970
  });
57824
57971
  }
57825
57972
  }
57826
57973
  return new basic_1.StructureType(components, parsedData.definitionName, parsedData.definitionName, parsedData.description);
57827
57974
  }
57828
57975
  }
57976
+ /** unprefixed field, take the type from the first source containing it, void or unknown sources are skipped */
57977
+ findInSources(ddic, parsedData, nameInSource) {
57978
+ if (nameInSource === undefined) {
57979
+ return undefined;
57980
+ }
57981
+ for (const source of parsedData.sources) {
57982
+ const lookup = ddic.lookupTableOrView(source.name);
57983
+ if (lookup.type instanceof basic_1.StructureType) {
57984
+ const type = lookup.type.getComponentByName(nameInSource);
57985
+ if (type) {
57986
+ return type;
57987
+ }
57988
+ }
57989
+ }
57990
+ return undefined;
57991
+ }
57829
57992
  }
57830
57993
  exports.CDSDetermineTypes = CDSDetermineTypes;
57831
57994
  //# sourceMappingURL=cds_determine_types.js.map
@@ -61388,8 +61551,8 @@ var __importStar = (this && this.__importStar) || (function () {
61388
61551
  };
61389
61552
  })();
61390
61553
  Object.defineProperty(exports, "__esModule", ({ value: true }));
61391
- exports.ABAPObject = exports.Tokens = exports.ExpressionsCDS = exports.CDSParser = exports.LanguageServerTypes = exports.DDLKind = exports.DDLParser = exports.AbstractFile = exports.ABAPFile = exports.MemoryFile = exports.Edits = exports.ReferenceType = exports.NativeSQL = exports.MacroContent = exports.MacroCall = exports.SpaghettiScopeNode = exports.Token = exports.ScopeType = exports.BasicTypes = exports.TypedIdentifier = exports.AbstractType = exports.VirtualPosition = exports.Comment = exports.Unknown = exports.Empty = exports.Identifier = exports.Nodes = exports.Types = exports.Expressions = exports.Statements = exports.Structures = exports.SkipLogic = exports.Objects = exports.ArtifactsRules = exports.ArtifactsObjects = exports.ArtifactsABAP = exports.BuiltIn = exports.FormLengthStats = exports.FunctionLengthStats = exports.MethodLengthStats = exports.LanguageServer = exports.Registry = exports.CyclomaticComplexityStats = exports.versionToABAPRelease = exports.releaseAtLeast = exports.Release = exports.LanguageVersion = exports.Version = exports.Config = exports.Issue = void 0;
61392
- exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = exports.CurrentScope = exports.RulesRunner = exports.SpaghettiScope = exports.SyntaxLogic = void 0;
61554
+ exports.Tokens = exports.ABAPParser = exports.ExpressionsCDS = exports.CDSParser = exports.LanguageServerTypes = exports.DDLKind = exports.DDLParser = exports.AbstractFile = exports.ABAPFile = exports.MemoryFile = exports.Edits = exports.ReferenceType = exports.NativeSQL = exports.MacroContent = exports.MacroCall = exports.SpaghettiScopeNode = exports.Token = exports.ScopeType = exports.BasicTypes = exports.TypedIdentifier = exports.AbstractType = exports.VirtualPosition = exports.Comment = exports.Unknown = exports.Empty = exports.Identifier = exports.Nodes = exports.Types = exports.Expressions = exports.Statements = exports.Structures = exports.SkipLogic = exports.Objects = exports.ArtifactsRules = exports.ArtifactsObjects = exports.ArtifactsABAP = exports.BuiltIn = exports.FormLengthStats = exports.FunctionLengthStats = exports.MethodLengthStats = exports.LanguageServer = exports.Registry = exports.CyclomaticComplexityStats = exports.versionToABAPRelease = exports.releaseAtLeast = exports.Release = exports.LanguageVersion = exports.Version = exports.Config = exports.Issue = void 0;
61555
+ exports.LSPEdit = exports.RuleTag = exports.Severity = exports.Visibility = exports.Info = exports.Diagnostics = exports.Rename = exports.PrettyPrinter = exports.Position = exports.CurrentScope = exports.RulesRunner = exports.SpaghettiScope = exports.SyntaxLogic = exports.ABAPObject = void 0;
61393
61556
  const issue_1 = __webpack_require__(/*! ./issue */ "./node_modules/@abaplint/core/build/src/issue.js");
61394
61557
  Object.defineProperty(exports, "Issue", { enumerable: true, get: function () { return issue_1.Issue; } });
61395
61558
  const config_1 = __webpack_require__(/*! ./config */ "./node_modules/@abaplint/core/build/src/config.js");
@@ -61486,6 +61649,8 @@ Object.defineProperty(exports, "DDLParser", { enumerable: true, get: function ()
61486
61649
  Object.defineProperty(exports, "DDLKind", { enumerable: true, get: function () { return ddl_parser_1.DDLKind; } });
61487
61650
  const cds_parser_1 = __webpack_require__(/*! ./cds/cds_parser */ "./node_modules/@abaplint/core/build/src/cds/cds_parser.js");
61488
61651
  Object.defineProperty(exports, "CDSParser", { enumerable: true, get: function () { return cds_parser_1.CDSParser; } });
61652
+ const abap_parser_1 = __webpack_require__(/*! ./abap/abap_parser */ "./node_modules/@abaplint/core/build/src/abap/abap_parser.js");
61653
+ Object.defineProperty(exports, "ABAPParser", { enumerable: true, get: function () { return abap_parser_1.ABAPParser; } });
61489
61654
  const rules_runner_1 = __webpack_require__(/*! ./rules_runner */ "./node_modules/@abaplint/core/build/src/rules_runner.js");
61490
61655
  Object.defineProperty(exports, "RulesRunner", { enumerable: true, get: function () { return rules_runner_1.RulesRunner; } });
61491
61656
  const _irule_1 = __webpack_require__(/*! ./rules/_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
@@ -66389,8 +66554,12 @@ exports.DataControl = DataControl;
66389
66554
 
66390
66555
  Object.defineProperty(exports, "__esModule", ({ value: true }));
66391
66556
  exports.DataDefinition = void 0;
66557
+ const tokens_1 = __webpack_require__(/*! ../abap/1_lexer/tokens */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/tokens/index.js");
66558
+ const combi_1 = __webpack_require__(/*! ../abap/2_statements/combi */ "./node_modules/@abaplint/core/build/src/abap/2_statements/combi.js");
66392
66559
  const nodes_1 = __webpack_require__(/*! ../abap/nodes */ "./node_modules/@abaplint/core/build/src/abap/nodes/index.js");
66393
66560
  const cds_determine_types_1 = __webpack_require__(/*! ../cds/cds_determine_types */ "./node_modules/@abaplint/core/build/src/cds/cds_determine_types.js");
66561
+ const cds_lexer_1 = __webpack_require__(/*! ../cds/cds_lexer */ "./node_modules/@abaplint/core/build/src/cds/cds_lexer.js");
66562
+ const version_1 = __webpack_require__(/*! ../version */ "./node_modules/@abaplint/core/build/src/version.js");
66394
66563
  const cds_parser_1 = __webpack_require__(/*! ../cds/cds_parser */ "./node_modules/@abaplint/core/build/src/cds/cds_parser.js");
66395
66564
  const expressions_1 = __webpack_require__(/*! ../cds/expressions */ "./node_modules/@abaplint/core/build/src/cds/expressions/index.js");
66396
66565
  const _abstract_object_1 = __webpack_require__(/*! ./_abstract_object */ "./node_modules/@abaplint/core/build/src/objects/_abstract_object.js");
@@ -66482,8 +66651,8 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
66482
66651
  associations: [],
66483
66652
  tree: undefined,
66484
66653
  };
66485
- this.findSQLViewName();
66486
66654
  this.parsedData.tree = new cds_parser_1.CDSParser().parse(this.findSourceFile());
66655
+ this.findSQLViewName(this.parsedData.tree);
66487
66656
  if (this.parsedData.tree) {
66488
66657
  for (const c of ((_a = this.parsedData.tree) === null || _a === void 0 ? void 0 : _a.getChildren()) || []) {
66489
66658
  if (c.get() instanceof expressions_1.CDSAnnotation) {
@@ -66508,11 +66677,30 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
66508
66677
  return (_a = this.parsedData) === null || _a === void 0 ? void 0 : _a.tree;
66509
66678
  }
66510
66679
  //////////
66511
- findSQLViewName() {
66680
+ findSQLViewName(tree) {
66512
66681
  var _a;
66513
- const match = (_a = this.findSourceFile()) === null || _a === void 0 ? void 0 : _a.getRaw().match(/@AbapCatalog\.sqlViewName: '([\w/]+)'/);
66514
- if (match) {
66515
- this.parsedData.sqlViewName = match[1].toUpperCase();
66682
+ let annotations = tree === null || tree === void 0 ? void 0 : tree.findDirectExpressions(expressions_1.CDSAnnotation);
66683
+ if (annotations === undefined) {
66684
+ const file = this.findSourceFile();
66685
+ if (file === undefined) {
66686
+ return;
66687
+ }
66688
+ // Keep SQL view lookup available even when the view body cannot be parsed.
66689
+ const tokens = cds_lexer_1.CDSLexer.run(file).filter(t => !(t instanceof tokens_1.Comment));
66690
+ const leadingAnnotations = (0, combi_1.seq)((0, combi_1.starPrio)(expressions_1.CDSAnnotation), (0, combi_1.starPrio)((0, combi_1.regex)(/.*/)));
66691
+ annotations = (_a = combi_1.Combi.run(leadingAnnotations, tokens, version_1.defaultRelease)) === null || _a === void 0 ? void 0 : _a.filter((n) => n instanceof nodes_1.ExpressionNode && n.get() instanceof expressions_1.CDSAnnotation);
66692
+ }
66693
+ for (const annotation of annotations || []) {
66694
+ const tokens = annotation.getTokens().map(t => t.getStr());
66695
+ const colon = tokens.indexOf(":");
66696
+ if (tokens.slice(0, colon).join("") !== "@AbapCatalog.sqlViewName") {
66697
+ continue;
66698
+ }
66699
+ const match = tokens.slice(colon + 1).join(" ").match(/^'([\w/]+)'$/);
66700
+ if (match) {
66701
+ this.parsedData.sqlViewName = match[1].toUpperCase();
66702
+ return;
66703
+ }
66516
66704
  }
66517
66705
  }
66518
66706
  findDescription() {
@@ -66538,12 +66726,16 @@ class DataDefinition extends _abstract_object_1.AbstractObject {
66538
66726
  let found = (_a = e.findDirectExpression(expressions_1.CDSAs)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(expressions_1.CDSName);
66539
66727
  const list = (_b = e.findDirectExpression(expressions_1.CDSPrefixedName)) === null || _b === void 0 ? void 0 : _b.findAllExpressions(expressions_1.CDSName);
66540
66728
  if (list !== undefined) {
66541
- const redirected = list.length > 1 && e.concatTokens().toUpperCase().includes(" REDIRECTED TO ");
66729
+ const redirected = e.concatTokens().toUpperCase().includes(" REDIRECTED TO ");
66542
66730
  if (list.length > 1 && redirected === false) {
66543
66731
  // "prefix.field" is looked up as "field" in "prefix", also when aliased via "as"
66544
66732
  prefix = list[0].concatTokens();
66545
66733
  nameInSource = list[list.length - 1].concatTokens();
66546
66734
  }
66735
+ else if (list.length === 1 && redirected === false) {
66736
+ // unprefixed "field" is looked up in the sources, also when aliased via "as"
66737
+ nameInSource = list[0].concatTokens();
66738
+ }
66547
66739
  if (found === undefined) {
66548
66740
  found = redirected === true ? list[0] : list[list.length - 1];
66549
66741
  }
@@ -73726,7 +73918,7 @@ class Registry {
73726
73918
  }
73727
73919
  static abaplintVersion() {
73728
73920
  // magic, see build script "version.js"
73729
- return "2.120.87";
73921
+ return "2.120.91";
73730
73922
  }
73731
73923
  getDDICReferences() {
73732
73924
  return this.ddicReferences;
@@ -76383,6 +76575,149 @@ exports.AbapdocParameterNames = AbapdocParameterNames;
76383
76575
 
76384
76576
  /***/ },
76385
76577
 
76578
+ /***/ "./node_modules/@abaplint/core/build/src/rules/abapdoc_short_link.js"
76579
+ /*!***************************************************************************!*\
76580
+ !*** ./node_modules/@abaplint/core/build/src/rules/abapdoc_short_link.js ***!
76581
+ \***************************************************************************/
76582
+ (__unused_webpack_module, exports, __webpack_require__) {
76583
+
76584
+ "use strict";
76585
+
76586
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
76587
+ if (k2 === undefined) k2 = k;
76588
+ var desc = Object.getOwnPropertyDescriptor(m, k);
76589
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
76590
+ desc = { enumerable: true, get: function() { return m[k]; } };
76591
+ }
76592
+ Object.defineProperty(o, k2, desc);
76593
+ }) : (function(o, m, k, k2) {
76594
+ if (k2 === undefined) k2 = k;
76595
+ o[k2] = m[k];
76596
+ }));
76597
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
76598
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
76599
+ }) : function(o, v) {
76600
+ o["default"] = v;
76601
+ });
76602
+ var __importStar = (this && this.__importStar) || (function () {
76603
+ var ownKeys = function(o) {
76604
+ ownKeys = Object.getOwnPropertyNames || function (o) {
76605
+ var ar = [];
76606
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
76607
+ return ar;
76608
+ };
76609
+ return ownKeys(o);
76610
+ };
76611
+ return function (mod) {
76612
+ if (mod && mod.__esModule) return mod;
76613
+ var result = {};
76614
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
76615
+ __setModuleDefault(result, mod);
76616
+ return result;
76617
+ };
76618
+ })();
76619
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
76620
+ exports.AbapdocShortLink = exports.AbapdocShortLinkConf = void 0;
76621
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
76622
+ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
76623
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
76624
+ const _abap_rule_1 = __webpack_require__(/*! ./_abap_rule */ "./node_modules/@abaplint/core/build/src/rules/_abap_rule.js");
76625
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
76626
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
76627
+ const _statement_1 = __webpack_require__(/*! ../abap/2_statements/statements/_statement */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/_statement.js");
76628
+ const position_1 = __webpack_require__(/*! ../position */ "./node_modules/@abaplint/core/build/src/position.js");
76629
+ const edit_helper_1 = __webpack_require__(/*! ../edit_helper */ "./node_modules/@abaplint/core/build/src/edit_helper.js");
76630
+ class AbapdocShortLinkConf extends _basic_rule_config_1.BasicRuleConfig {
76631
+ }
76632
+ exports.AbapdocShortLinkConf = AbapdocShortLinkConf;
76633
+ class AbapdocShortLink extends _abap_rule_1.ABAPRule {
76634
+ constructor() {
76635
+ super(...arguments);
76636
+ this.conf = new AbapdocShortLinkConf();
76637
+ }
76638
+ getMetadata() {
76639
+ return {
76640
+ key: "abapdoc_short_link",
76641
+ title: "Use short form of ABAP Doc links",
76642
+ shortDescription: `Reports ABAP Doc links to components of the current global class or interface which repeat the object name.`,
76643
+ extendedInformation: `Links to components of the same repository object can omit the object name and start with a period,
76644
+ {@link zif_foo.data:field} can be written as {@link .data:field}.
76645
+
76646
+ Only checks ABAP Doc inside the definition of the global class or interface.
76647
+
76648
+ https://github.com/SAP/abap-file-formats/issues/880`,
76649
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
76650
+ badExample: `INTERFACE zif_foo PUBLIC.
76651
+ "! Same as {@link zif_foo.data:bar}
76652
+ DATA foo TYPE string.
76653
+ DATA bar TYPE string.
76654
+ ENDINTERFACE.`,
76655
+ goodExample: `INTERFACE zif_foo PUBLIC.
76656
+ "! Same as {@link .data:bar}
76657
+ DATA foo TYPE string.
76658
+ DATA bar TYPE string.
76659
+ ENDINTERFACE.`,
76660
+ };
76661
+ }
76662
+ getConfig() {
76663
+ return this.conf;
76664
+ }
76665
+ setConfig(conf) {
76666
+ this.conf = conf;
76667
+ }
76668
+ runParsed(file, obj) {
76669
+ var _a;
76670
+ const issues = [];
76671
+ const objectType = obj.getType();
76672
+ if (objectType !== "CLAS" && objectType !== "INTF") {
76673
+ return [];
76674
+ }
76675
+ const objectName = obj.getName().toLowerCase();
76676
+ let definition = false;
76677
+ for (const statement of file.getStatements()) {
76678
+ const type = statement.get();
76679
+ if (type instanceof Statements.ClassDefinition || type instanceof Statements.Interface) {
76680
+ const name = (_a = statement.findFirstExpression(Expressions.ClassName)) !== null && _a !== void 0 ? _a : statement.findFirstExpression(Expressions.InterfaceName);
76681
+ definition = (name === null || name === void 0 ? void 0 : name.getFirstToken().getStr().toLowerCase()) === objectName;
76682
+ continue;
76683
+ }
76684
+ else if (type instanceof Statements.EndClass || type instanceof Statements.EndInterface) {
76685
+ definition = false;
76686
+ continue;
76687
+ }
76688
+ else if (definition === false || !(type instanceof _statement_1.Comment)) {
76689
+ continue;
76690
+ }
76691
+ const token = statement.getFirstToken();
76692
+ const str = token.getStr();
76693
+ if (str.startsWith(`"!`) === false) {
76694
+ continue;
76695
+ }
76696
+ const regex = /\{@link\s+((?:(\w+):)?([\w/]+))\./gi;
76697
+ let match;
76698
+ while ((match = regex.exec(str)) !== null) {
76699
+ const kind = match[2];
76700
+ if (match[3].toLowerCase() !== objectName
76701
+ || (kind !== undefined && kind.toLowerCase() !== objectType.toLowerCase())) {
76702
+ continue;
76703
+ }
76704
+ const row = token.getStart().getRow();
76705
+ const col = token.getStart().getCol() + match.index + match[0].length - 1 - match[1].length;
76706
+ const start = new position_1.Position(row, col);
76707
+ const end = new position_1.Position(row, col + match[1].length);
76708
+ const fix = edit_helper_1.EditHelper.deleteRange(file, start, end);
76709
+ const message = `Use short form of ABAP Doc link, remove "${match[1]}"`;
76710
+ issues.push(issue_1.Issue.atRange(file, start, end, message, this.getMetadata().key, this.conf.severity, fix));
76711
+ }
76712
+ }
76713
+ return issues;
76714
+ }
76715
+ }
76716
+ exports.AbapdocShortLink = AbapdocShortLink;
76717
+ //# sourceMappingURL=abapdoc_short_link.js.map
76718
+
76719
+ /***/ },
76720
+
76386
76721
  /***/ "./node_modules/@abaplint/core/build/src/rules/abapdoc_shorttext_length.js"
76387
76722
  /*!*********************************************************************************!*\
76388
76723
  !*** ./node_modules/@abaplint/core/build/src/rules/abapdoc_shorttext_length.js ***!
@@ -85508,6 +85843,18 @@ Make sure to test the downported code, it might not always be completely correct
85508
85843
  }
85509
85844
  }
85510
85845
  }
85846
+ // a chained call with EXPORTING/IMPORTING/CHANGING/RECEIVING/EXCEPTIONS parses in the low
85847
+ // version, but 702 reads the last call of a chain as a functional call,
85848
+ // "Unexpected word CHANGING in functional method call"
85849
+ if (ret.length === 0) {
85850
+ for (let i = 0; i < lowStatements.length; i++) {
85851
+ const issue = this.outlineChainedCallParameters(lowStatements[i], highStatements[i], lowFile, highSyntax);
85852
+ if (issue) {
85853
+ ret.push(issue);
85854
+ break;
85855
+ }
85856
+ }
85857
+ }
85511
85858
  // INSERT with a 702 built-in function as the operand parses in the low version, but 702
85512
85859
  // reads the function as a method call, "Method SUBSTRING does not exist"
85513
85860
  if (ret.length === 0) {
@@ -88089,6 +88436,67 @@ ${indentation} output = ${uniqueName}.\n`;
88089
88436
  }
88090
88437
  return undefined;
88091
88438
  }
88439
+ /** a( )->b( CHANGING c = d ), the object the last method is called on is outlined into a
88440
+ * variable typed via the RETURNING parameter of the method before it */
88441
+ outlineChainedCallParameters(low, high, lowFile, highSyntax) {
88442
+ var _a, _b, _c, _d;
88443
+ if (!(high.get() instanceof Statements.Call)) {
88444
+ return undefined;
88445
+ }
88446
+ else if (this.lowReg.getConfig().isOpenABAP()) {
88447
+ return undefined;
88448
+ }
88449
+ else if (low.getFirstToken().getStart() instanceof virtual_position_1.VirtualPosition) {
88450
+ return undefined;
88451
+ }
88452
+ const chain = high.findDirectExpression(Expressions.MethodCallChain);
88453
+ const children = (chain === null || chain === void 0 ? void 0 : chain.getChildren()) || [];
88454
+ const last = children[children.length - 1];
88455
+ const arrow = children[children.length - 2];
88456
+ const previous = children[children.length - 3];
88457
+ if (chain === undefined
88458
+ || !(last instanceof nodes_1.ExpressionNode)
88459
+ || !(last.get() instanceof Expressions.MethodCall)
88460
+ || (arrow === null || arrow === void 0 ? void 0 : arrow.getFirstToken().getStr()) !== "->"
88461
+ || !(previous instanceof nodes_1.ExpressionNode)
88462
+ || !(previous.get() instanceof Expressions.MethodCall)) {
88463
+ return undefined;
88464
+ }
88465
+ const parameters = (_a = last.findDirectExpression(Expressions.MethodCallParam)) === null || _a === void 0 ? void 0 : _a.findDirectExpression(Expressions.MethodParameters);
88466
+ if (parameters === undefined) {
88467
+ return undefined;
88468
+ }
88469
+ // the type of the object, from the method that returns it
88470
+ const methodToken = (_b = previous.findDirectExpression(Expressions.MethodName)) === null || _b === void 0 ? void 0 : _b.getFirstToken();
88471
+ if (methodToken === undefined) {
88472
+ return undefined;
88473
+ }
88474
+ const spag = highSyntax.spaghetti.lookupPosition(methodToken.getStart(), lowFile.getFilename());
88475
+ let type = undefined;
88476
+ for (const r of (spag === null || spag === void 0 ? void 0 : spag.getData().references) || []) {
88477
+ if (r.referenceType === _reference_1.ReferenceType.MethodReference
88478
+ && r.position.getStart().equals(methodToken.getStart())) {
88479
+ const returning = (_d = (_c = r.resolved) === null || _c === void 0 ? void 0 : _c.getParameters().getReturning()) === null || _d === void 0 ? void 0 : _d.getType();
88480
+ if (returning instanceof basic_1.ObjectReferenceType) {
88481
+ type = returning.toABAP();
88482
+ }
88483
+ break;
88484
+ }
88485
+ }
88486
+ if (type === undefined) {
88487
+ return undefined;
88488
+ }
88489
+ const object = children.slice(0, children.length - 2).map(c => c.concatTokens()).join("");
88490
+ const uniqueName = this.uniqueName(high.getFirstToken().getStart(), lowFile.getFilename(), highSyntax);
88491
+ const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
88492
+ const code = `DATA ${uniqueName} TYPE ${type}.\n` +
88493
+ indentation + `${uniqueName} = ${object}.\n` +
88494
+ indentation;
88495
+ const fix1 = edit_helper_1.EditHelper.insertAt(lowFile, high.getFirstToken().getStart(), code);
88496
+ const fix2 = edit_helper_1.EditHelper.replaceRange(lowFile, chain.getFirstToken().getStart(), previous.getLastToken().getEnd(), uniqueName);
88497
+ const fix = edit_helper_1.EditHelper.merge(fix2, fix1);
88498
+ return issue_1.Issue.atToken(lowFile, chain.getFirstToken(), "Outline object of chained call with parameter keywords", this.getMetadata().key, this.conf.severity, fix);
88499
+ }
88092
88500
  /** if the source expression is the complete source of an INSERT or APPEND statement, then the
88093
88501
  * outlined variable must be typed via the row type of the table, returns the table name */
88094
88502
  findRowTypeOf(high, source) {
@@ -88491,12 +88899,9 @@ ${indentation} output = ${uniqueName}.\n`;
88491
88899
  }
88492
88900
  }
88493
88901
  /** NEW into a data reference creates data, CREATE OBJECT cannot: "target = NEW #|type( [value] )"
88494
- * with a variable typed REF TO a data type becomes CREATE DATA plus the assignment of the value */
88902
+ * with a target typed REF TO a data type becomes CREATE DATA plus the assignment of the value */
88495
88903
  newDataReference(high, target, found, lowFile, highSyntax) {
88496
88904
  var _a, _b, _c;
88497
- if (target.getChildren().length !== 1 || !(((_a = target.getFirstChild()) === null || _a === void 0 ? void 0 : _a.get()) instanceof Expressions.TargetField)) {
88498
- return undefined;
88499
- }
88500
88905
  const children = found.getChildren();
88501
88906
  const value = found.findDirectExpression(Expressions.Source);
88502
88907
  // NEW, the type, "(", optionally one value, ")"
@@ -88504,12 +88909,23 @@ ${indentation} output = ${uniqueName}.\n`;
88504
88909
  return undefined;
88505
88910
  }
88506
88911
  const name = target.concatTokens();
88912
+ const typeExpr = found.findDirectExpression(Expressions.TypeNameOrInfer);
88913
+ const typeName = typeExpr === null || typeExpr === void 0 ? void 0 : typeExpr.concatTokens();
88507
88914
  const spag = highSyntax.spaghetti.lookupPosition(high.getFirstToken().getStart(), lowFile.getFilename());
88508
- const type = (_b = spag === null || spag === void 0 ? void 0 : spag.findVariable(name)) === null || _b === void 0 ? void 0 : _b.getType();
88915
+ let type = undefined;
88916
+ if (typeName === "#") {
88917
+ // the type of the target, as inferred by the syntax check
88918
+ const typeToken = typeExpr.getFirstToken();
88919
+ const resolved = (_a = spag === null || spag === void 0 ? void 0 : spag.getData().references.find(r => r.referenceType === _reference_1.ReferenceType.InferredType
88920
+ && r.position.getStart().equals(typeToken.getStart()))) === null || _a === void 0 ? void 0 : _a.resolved;
88921
+ type = resolved instanceof _typed_identifier_1.TypedIdentifier ? resolved.getType() : undefined;
88922
+ }
88923
+ else if (target.getChildren().length === 1 && ((_b = target.getFirstChild()) === null || _b === void 0 ? void 0 : _b.get()) instanceof Expressions.TargetField) {
88924
+ type = (_c = spag === null || spag === void 0 ? void 0 : spag.findVariable(name)) === null || _c === void 0 ? void 0 : _c.getType();
88925
+ }
88509
88926
  if (!(type instanceof basic_1.DataReference)) {
88510
88927
  return undefined;
88511
88928
  }
88512
- const typeName = (_c = found.findDirectExpression(Expressions.TypeNameOrInfer)) === null || _c === void 0 ? void 0 : _c.concatTokens();
88513
88929
  const indentation = " ".repeat(high.getFirstToken().getStart().getCol() - 1);
88514
88930
  let abap = `CREATE DATA ${name}` + (typeName === "#" ? "" : ` TYPE ${typeName}`) + ".";
88515
88931
  if (value === undefined) {
@@ -88557,7 +88973,7 @@ ${indentation} output = ${uniqueName}.\n`;
88557
88973
  }
88558
88974
  }
88559
88975
  else {
88560
- throw new Error("newParameters, downport, unexpected");
88976
+ return undefined; // not an object, CREATE OBJECT does not apply
88561
88977
  }
88562
88978
  }
88563
88979
  if (cdef && cdef.getMethodDefinitions === undefined) {
@@ -90064,6 +90480,121 @@ exports.ForbiddenIdentifier = ForbiddenIdentifier;
90064
90480
 
90065
90481
  /***/ },
90066
90482
 
90483
+ /***/ "./node_modules/@abaplint/core/build/src/rules/forbidden_kernel_write.js"
90484
+ /*!*******************************************************************************!*\
90485
+ !*** ./node_modules/@abaplint/core/build/src/rules/forbidden_kernel_write.js ***!
90486
+ \*******************************************************************************/
90487
+ (__unused_webpack_module, exports, __webpack_require__) {
90488
+
90489
+ "use strict";
90490
+
90491
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
90492
+ if (k2 === undefined) k2 = k;
90493
+ var desc = Object.getOwnPropertyDescriptor(m, k);
90494
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
90495
+ desc = { enumerable: true, get: function() { return m[k]; } };
90496
+ }
90497
+ Object.defineProperty(o, k2, desc);
90498
+ }) : (function(o, m, k, k2) {
90499
+ if (k2 === undefined) k2 = k;
90500
+ o[k2] = m[k];
90501
+ }));
90502
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
90503
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
90504
+ }) : function(o, v) {
90505
+ o["default"] = v;
90506
+ });
90507
+ var __importStar = (this && this.__importStar) || (function () {
90508
+ var ownKeys = function(o) {
90509
+ ownKeys = Object.getOwnPropertyNames || function (o) {
90510
+ var ar = [];
90511
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
90512
+ return ar;
90513
+ };
90514
+ return ownKeys(o);
90515
+ };
90516
+ return function (mod) {
90517
+ if (mod && mod.__esModule) return mod;
90518
+ var result = {};
90519
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
90520
+ __setModuleDefault(result, mod);
90521
+ return result;
90522
+ };
90523
+ })();
90524
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
90525
+ exports.ForbiddenKernelWrite = exports.ForbiddenKernelWriteConf = void 0;
90526
+ const Expressions = __importStar(__webpack_require__(/*! ../abap/2_statements/expressions */ "./node_modules/@abaplint/core/build/src/abap/2_statements/expressions/index.js"));
90527
+ const Statements = __importStar(__webpack_require__(/*! ../abap/2_statements/statements */ "./node_modules/@abaplint/core/build/src/abap/2_statements/statements/index.js"));
90528
+ const _abap_rule_1 = __webpack_require__(/*! ./_abap_rule */ "./node_modules/@abaplint/core/build/src/rules/_abap_rule.js");
90529
+ const _basic_rule_config_1 = __webpack_require__(/*! ./_basic_rule_config */ "./node_modules/@abaplint/core/build/src/rules/_basic_rule_config.js");
90530
+ const issue_1 = __webpack_require__(/*! ../issue */ "./node_modules/@abaplint/core/build/src/issue.js");
90531
+ const _irule_1 = __webpack_require__(/*! ./_irule */ "./node_modules/@abaplint/core/build/src/rules/_irule.js");
90532
+ class ForbiddenKernelWriteConf extends _basic_rule_config_1.BasicRuleConfig {
90533
+ constructor() {
90534
+ super(...arguments);
90535
+ /** Objects allowed to use WRITE '@KERNEL', array of string regex matched against the object name.
90536
+ * Empty, the default, allows every object
90537
+ * @uniqueItems true
90538
+ */
90539
+ this.allow = [];
90540
+ }
90541
+ }
90542
+ exports.ForbiddenKernelWriteConf = ForbiddenKernelWriteConf;
90543
+ class ForbiddenKernelWrite extends _abap_rule_1.ABAPRule {
90544
+ constructor() {
90545
+ super(...arguments);
90546
+ this.conf = new ForbiddenKernelWriteConf();
90547
+ this.allow = [];
90548
+ }
90549
+ getMetadata() {
90550
+ return {
90551
+ key: "forbidden_kernel_write",
90552
+ title: "Forbidden kernel WRITE",
90553
+ shortDescription: `Forbid WRITE '@KERNEL' outside the listed objects`,
90554
+ extendedInformation: `The transpiler emits the literal of WRITE '@KERNEL ...' as raw JavaScript,
90555
+ which runs with the privileges of the host. Open-abap uses this for its kernel classes, code compiled by a host it did not write should not.
90556
+
90557
+ allow is a list of regex, case insensitive, matched against the object name, eg. ["^CL_", "^KERNEL_"].
90558
+ An empty list, the default, allows every object. To forbid WRITE '@KERNEL' everywhere use a regex matching no name, eg. ["^$"].`,
90559
+ tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Security],
90560
+ badExample: `WRITE '@KERNEL process.exit(1);'.`,
90561
+ };
90562
+ }
90563
+ getConfig() {
90564
+ return this.conf;
90565
+ }
90566
+ setConfig(conf) {
90567
+ this.conf = conf;
90568
+ if (this.conf.allow === undefined) {
90569
+ this.conf.allow = [];
90570
+ }
90571
+ this.allow = this.conf.allow.map(a => new RegExp(a, "i"));
90572
+ }
90573
+ runParsed(file, obj) {
90574
+ var _a;
90575
+ if (this.allow.length === 0 || this.allow.some(a => a.test(obj.getName()))) {
90576
+ return [];
90577
+ }
90578
+ const ret = [];
90579
+ for (const s of file.getStatements()) {
90580
+ if (!(s.get() instanceof Statements.Write)) {
90581
+ continue;
90582
+ }
90583
+ // same check as the transpiler, statements/write.ts
90584
+ const source = (_a = s.findDirectExpression(Expressions.Source)) === null || _a === void 0 ? void 0 : _a.concatTokens();
90585
+ if ((source === null || source === void 0 ? void 0 : source.startsWith("'@KERNEL ")) === true) {
90586
+ const message = "WRITE '@KERNEL' not allowed in " + obj.getName();
90587
+ ret.push(issue_1.Issue.atStatement(file, s, message, this.getMetadata().key, this.conf.severity));
90588
+ }
90589
+ }
90590
+ return ret;
90591
+ }
90592
+ }
90593
+ exports.ForbiddenKernelWrite = ForbiddenKernelWrite;
90594
+ //# sourceMappingURL=forbidden_kernel_write.js.map
90595
+
90596
+ /***/ },
90597
+
90067
90598
  /***/ "./node_modules/@abaplint/core/build/src/rules/forbidden_pseudo_and_pragma.js"
90068
90599
  /*!************************************************************************************!*\
90069
90600
  !*** ./node_modules/@abaplint/core/build/src/rules/forbidden_pseudo_and_pragma.js ***!
@@ -92960,6 +93491,7 @@ __exportStar(__webpack_require__(/*! ./abapdoc */ "./node_modules/@abaplint/core
92960
93491
  __exportStar(__webpack_require__(/*! ./abapdoc_html_tag */ "./node_modules/@abaplint/core/build/src/rules/abapdoc_html_tag.js"), exports);
92961
93492
  __exportStar(__webpack_require__(/*! ./abapdoc_leading_at */ "./node_modules/@abaplint/core/build/src/rules/abapdoc_leading_at.js"), exports);
92962
93493
  __exportStar(__webpack_require__(/*! ./abapdoc_parameter_names */ "./node_modules/@abaplint/core/build/src/rules/abapdoc_parameter_names.js"), exports);
93494
+ __exportStar(__webpack_require__(/*! ./abapdoc_short_link */ "./node_modules/@abaplint/core/build/src/rules/abapdoc_short_link.js"), exports);
92963
93495
  __exportStar(__webpack_require__(/*! ./abapdoc_shorttext_length */ "./node_modules/@abaplint/core/build/src/rules/abapdoc_shorttext_length.js"), exports);
92964
93496
  __exportStar(__webpack_require__(/*! ./add_test_attributes */ "./node_modules/@abaplint/core/build/src/rules/add_test_attributes.js"), exports);
92965
93497
  __exportStar(__webpack_require__(/*! ./aff_and_xml */ "./node_modules/@abaplint/core/build/src/rules/aff_and_xml.js"), exports);
@@ -93034,6 +93566,7 @@ __exportStar(__webpack_require__(/*! ./exporting */ "./node_modules/@abaplint/co
93034
93566
  __exportStar(__webpack_require__(/*! ./final_newline */ "./node_modules/@abaplint/core/build/src/rules/final_newline.js"), exports);
93035
93567
  __exportStar(__webpack_require__(/*! ./fm_global_parameters_obsolete */ "./node_modules/@abaplint/core/build/src/rules/fm_global_parameters_obsolete.js"), exports);
93036
93568
  __exportStar(__webpack_require__(/*! ./forbidden_identifier */ "./node_modules/@abaplint/core/build/src/rules/forbidden_identifier.js"), exports);
93569
+ __exportStar(__webpack_require__(/*! ./forbidden_kernel_write */ "./node_modules/@abaplint/core/build/src/rules/forbidden_kernel_write.js"), exports);
93037
93570
  __exportStar(__webpack_require__(/*! ./forbidden_pseudo_and_pragma */ "./node_modules/@abaplint/core/build/src/rules/forbidden_pseudo_and_pragma.js"), exports);
93038
93571
  __exportStar(__webpack_require__(/*! ./forbidden_void_type */ "./node_modules/@abaplint/core/build/src/rules/forbidden_void_type.js"), exports);
93039
93572
  __exportStar(__webpack_require__(/*! ./form_tables_obsolete */ "./node_modules/@abaplint/core/build/src/rules/form_tables_obsolete.js"), exports);
@@ -102915,7 +103448,7 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102915
103448
  continue;
102916
103449
  }
102917
103450
  const operands = this.flattenChain(source);
102918
- if (!operands.some(op => { const c = op.getFirstToken().getStr()[0]; return c === "'" || c === "`"; })) {
103451
+ if (!operands.some(op => this.isLiteral(op))) {
102919
103452
  continue;
102920
103453
  }
102921
103454
  const template = this.buildTemplate(operands);
@@ -102940,10 +103473,11 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102940
103473
  buildTemplate(operands) {
102941
103474
  let inner = "";
102942
103475
  for (const op of operands) {
102943
- const raw = op.getFirstToken().getStr();
102944
- const firstChar = raw[0];
102945
- if (firstChar === "'" || firstChar === "`") {
102946
- inner += raw.slice(1, -1);
103476
+ if (this.isLiteral(op)) {
103477
+ const raw = op.getFirstToken().getStr();
103478
+ const delimiter = raw[0];
103479
+ const value = raw.slice(1, -1).split(delimiter + delimiter).join(delimiter);
103480
+ inner += value.replace(/[\\|{}]/g, "\\$&");
102947
103481
  }
102948
103482
  else {
102949
103483
  inner += `{ ${op.concatTokens()} }`;
@@ -102951,6 +103485,14 @@ class PreferStringTemplate extends _abap_rule_1.ABAPRule {
102951
103485
  }
102952
103486
  return `|${inner}|`;
102953
103487
  }
103488
+ // plain character or string literal, ie. not text symbols or concatenated constants
103489
+ isLiteral(op) {
103490
+ if (op.getFirstToken() !== op.getLastToken()) {
103491
+ return false;
103492
+ }
103493
+ const firstChar = op.getFirstToken().getStr()[0];
103494
+ return firstChar === "'" || firstChar === "`";
103495
+ }
102954
103496
  }
102955
103497
  exports.PreferStringTemplate = PreferStringTemplate;
102956
103498
  //# sourceMappingURL=prefer_string_template.js.map
@@ -113071,6 +113613,7 @@ class XMLConsistency {
113071
113613
  extendedInformation: `Checks:
113072
113614
  * XML is well-formed and parseable
113073
113615
  * Naming for CLAS and INTF objects
113616
+ * CLAS and INTF objects do not contain fields that abapGit clears, eg. AUTHOR, CREATEDON and VERSION
113074
113617
  * QUAN fields in TABL objects have reference table and field values
113075
113618
  * Transparent TABL objects have delivery class and size category set
113076
113619
  * TABL fields with a built-in type carry the INTTYPE abapGit serializes for it, for the confirmed pairs
@@ -113156,6 +113699,22 @@ class XMLConsistency {
113156
113699
  else if (((_a = obj.getMainABAPFile()) === null || _a === void 0 ? void 0 : _a.getStructure()) !== undefined && obj.getClassDefinition() === undefined) {
113157
113700
  issues.push(issue_1.Issue.atRow(file, 1, "Class matching XML name not found in ABAP file", this.getMetadata().key, this.conf.severity));
113158
113701
  }
113702
+ issues.push(...this.checkClearedFields(obj, file, "VSEOCLASS", XMLConsistency.clearedClassFields));
113703
+ return issues;
113704
+ }
113705
+ checkClearedFields(obj, file, structure, fields) {
113706
+ var _a, _b;
113707
+ const issues = [];
113708
+ const block = (_b = (_a = obj.getXML()) === null || _a === void 0 ? void 0 : _a.match(new RegExp(`<${structure}>([\\s\\S]*?)</${structure}>`))) === null || _b === void 0 ? void 0 : _b[1];
113709
+ if (block === undefined) {
113710
+ return issues;
113711
+ }
113712
+ for (const field of fields) {
113713
+ if (block.match(new RegExp(`<${field}[\\s/>]`)) !== null) {
113714
+ const message = `Field ${field} in ${structure} is cleared by abapGit and should not be in the XML`;
113715
+ issues.push(issue_1.Issue.atRow(file, 1, message, this.getMetadata().key, this.conf.severity));
113716
+ }
113717
+ }
113159
113718
  return issues;
113160
113719
  }
113161
113720
  runTextPool(obj, file) {
@@ -113189,6 +113748,7 @@ class XMLConsistency {
113189
113748
  else if (obj.getDefinition() !== undefined && ((_a = obj.getDefinition()) === null || _a === void 0 ? void 0 : _a.getName().toUpperCase()) !== name.toUpperCase()) {
113190
113749
  issues.push(issue_1.Issue.atRow(file, 1, "Interface matching XML name not found in ABAP file", this.getMetadata().key, this.conf.severity));
113191
113750
  }
113751
+ issues.push(...this.checkClearedFields(obj, file, "VSEOINTERF", XMLConsistency.clearedInterfaceFields));
113192
113752
  return issues;
113193
113753
  }
113194
113754
  checkTextLength(file, fieldName, text, maxLength, lang) {
@@ -113369,6 +113929,14 @@ class XMLConsistency {
113369
113929
  }
113370
113930
  }
113371
113931
  exports.XMLConsistency = XMLConsistency;
113932
+ // fields abapGit clears before serializing, see ZCL_ABAPGIT_OO_CLASS and ZCL_ABAPGIT_OO_INTERFACE;
113933
+ // abapGit omits initial fields, so any occurrence shows as a diff on every pull
113934
+ XMLConsistency.clearedInterfaceFields = [
113935
+ "UUID", "AUTHOR", "CREATEDON", "CHANGEDBY", "CHANGEDON", "CHGDANYBY", "CHGDANYON", "R3RELEASE", "VERSION",
113936
+ ];
113937
+ XMLConsistency.clearedClassFields = [
113938
+ ...XMLConsistency.clearedInterfaceFields, "CLSFINAL", "CLSABSTRCT", "EXPOSURE",
113939
+ ];
113372
113940
  // INTTYPE that abapGit serializes for a built-in DATATYPE, confirmed by exported tables;
113373
113941
  // a different value activates, but shows as a diff on every pull
113374
113942
  XMLConsistency.intTypes = {
@@ -132417,6 +132985,9 @@ class SelectTranspiler {
132417
132985
  else if (node.findDirectTokenByText("DISTINCT") !== undefined) {
132418
132986
  throw new Error("SelectTranspiler, UNION DISTINCT todo");
132419
132987
  }
132988
+ else if (node.findDirectExpression(abaplint.Expressions.Select)?.findDirectTokenByText("UPDATE") !== undefined) {
132989
+ throw new Error("SELECT SINGLE FOR UPDATE, not supported, transpiler");
132990
+ }
132420
132991
  let target = "undefined";
132421
132992
  let intoCorrespondingStructure = false;
132422
132993
  if (targetOverride) {
package/build/types.d.ts CHANGED
@@ -35,6 +35,8 @@ export interface ITranspilerConfig {
35
35
  */
36
36
  name?: string;
37
37
  url?: string;
38
+ /** commit SHA, tag or branch to check out from url; defaults to the default branch */
39
+ ref?: string;
38
40
  /** relative to the current working directory */
39
41
  folder?: string;
40
42
  files?: string | string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abaplint/transpiler-cli",
3
- "version": "2.14.12",
3
+ "version": "2.14.14",
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.87",
31
- "@abaplint/transpiler": "^2.14.12",
30
+ "@abaplint/core": "^2.120.91",
31
+ "@abaplint/transpiler": "^2.14.14",
32
32
  "@types/glob": "^9.0.0",
33
33
  "@types/node": "^26.6.5",
34
34
  "@types/progress": "^2.0.7",