@abaplint/cli
Version:
abaplint - Command Line Interface
1,283 lines (1,216 loc) • 4.48 MB
JavaScript
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./build/src/apack_dependency_provider.js":
/*!************************************************!*\
!*** ./build/src/apack_dependency_provider.js ***!
\************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ApackDependencyProvider = void 0;
const xml_js_1 = __webpack_require__(/*! xml-js */ "./node_modules/xml-js/lib/index.js");
class ApackDependencyProvider {
static fromManifest(manifestContents) {
var _a, _b;
if (!manifestContents || !manifestContents.length) {
return [];
}
const result = [];
const manifest = (0, xml_js_1.xml2js)(manifestContents, { compact: true });
let apackDependencies = (_b = (_a = manifest["asx:abap"]["asx:values"]["DATA"]) === null || _a === void 0 ? void 0 : _a["DEPENDENCIES"]) === null || _b === void 0 ? void 0 : _b.item;
if (!apackDependencies) {
return [];
}
else if (!apackDependencies.length) {
apackDependencies = [apackDependencies];
}
for (const dependency of apackDependencies) {
result.push({
files: "/src/**/*.*",
url: dependency["GIT_URL"]["_text"],
});
}
return result;
}
}
exports.ApackDependencyProvider = ApackDependencyProvider;
//# sourceMappingURL=apack_dependency_provider.js.map
/***/ }),
/***/ "./build/src/compressed_file.js":
/*!**************************************!*\
!*** ./build/src/compressed_file.js ***!
\**************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CompressedFile = void 0;
const core_1 = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
const zlib = __webpack_require__(/*! zlib */ "zlib");
class CompressedFile extends core_1.AbstractFile {
constructor(filename, compressed) {
super(filename);
this.compressed = compressed;
}
getRaw() {
return this.decompress(this.compressed);
}
getRawRows() {
return this.decompress(this.compressed).split("\n");
}
decompress(compressed) {
return zlib.inflateSync(Buffer.from(compressed, "base64")).toString("utf8");
}
}
exports.CompressedFile = CompressedFile;
//# sourceMappingURL=compressed_file.js.map
/***/ }),
/***/ "./build/src/file_operations.js":
/*!**************************************!*\
!*** ./build/src/file_operations.js ***!
\**************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FileOperations = void 0;
const compressed_file_1 = __webpack_require__(/*! ./compressed_file */ "./build/src/compressed_file.js");
const core_1 = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
const fs = __webpack_require__(/*! node:fs */ "node:fs");
const fsPromises = __webpack_require__(/*! node:fs/promises */ "node:fs/promises");
const glob = __webpack_require__(/*! glob */ "./node_modules/glob/dist/commonjs/index.js");
const os = __webpack_require__(/*! node:os */ "node:os");
const path = __webpack_require__(/*! node:path */ "node:path");
const pLimit = __webpack_require__(/*! p-limit */ "./node_modules/p-limit/index.js");
const zlib = __webpack_require__(/*! node:zlib */ "node:zlib");
class FileOperations {
static deleteFolderRecursive(dir) {
if (fs.existsSync(dir) === false) {
return;
}
fs.rmSync(dir, { recursive: true });
}
static toUnixPath(path) {
if (os.platform() === "win32") {
return path.replace(/[\\/]+/g, "/").replace(/^([a-zA-Z]+:|\.\/)/, "");
}
else {
return path;
}
}
static loadFileNames(arg, error = true) {
const files = glob.sync(arg, { nodir: true, absolute: true, posix: true });
if (files.length === 0 && error) {
throw "Error: No files found, " + arg;
}
return files;
}
static async readFile(filename, compress) {
const raw = await fsPromises.readFile(filename, { encoding: "utf8" });
if (compress === true) {
// todo, util.promisify(zlib.deflate) does not seem to work?
return new compressed_file_1.CompressedFile(filename, zlib.deflateSync(raw).toString("base64"));
}
else {
return new core_1.MemoryFile(filename, raw);
}
}
static setupPLimit() {
let concurrency = os.cpus().length;
if (concurrency > 8) {
concurrency = 8;
}
else if (concurrency < 1) {
concurrency = 1;
}
return pLimit(concurrency);
}
static async loadFiles(compress, input, bar) {
const limit = this.setupPLimit();
input = input.filter((filename) => {
const base = filename.split("/").reverse()[0];
if (base.split(".").length <= 2) {
return false;
}
return true;
});
bar.set(input.length, "Reading files");
const promises = input.map((filename) => {
return limit(async () => {
bar.tick("Reading files - " + path.basename(filename));
return this.readFile(filename, compress);
});
});
const files = await Promise.all(promises);
return files;
}
}
exports.FileOperations = FileOperations;
//# sourceMappingURL=file_operations.js.map
/***/ }),
/***/ "./build/src/fixes.js":
/*!****************************!*\
!*** ./build/src/fixes.js ***!
\****************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ApplyFixes = void 0;
const core_1 = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
class ApplyFixes {
constructor() {
this.changedFiles = new Set();
}
// Strategy:
// Execute one rule at a time and apply fixes for that rule
// Some rules are quite expensive to initialize(like downport),
// so running all rules every time is expensive.
async applyFixes(reg, fs, extra) {
let iteration = 0;
this.changedFiles.clear();
const MAX_ITERATIONS = 50000;
const objects = new core_1.RulesRunner(reg).objectsToCheck(reg.getObjects());
const rules = reg.getConfig().getEnabledRules();
while (iteration <= MAX_ITERATIONS) {
let changed = 0;
for (const rule of rules) {
while (iteration <= MAX_ITERATIONS) {
const before = Date.now();
rule.initialize(reg);
let issues = [];
for (const obj of objects) {
issues.push(...rule.run(obj));
}
issues = new core_1.RulesRunner(reg).excludeIssues(issues);
iteration++;
const applied = this.applyList(issues, reg);
const appliedCount = applied.files.length;
const runtime = Date.now() - before;
if ((extra === null || extra === void 0 ? void 0 : extra.quiet) !== true) {
process.stderr.write(`\tIteration ${iteration.toString().padStart(3, " ")}, ${appliedCount.toString().padStart(3, " ")} fixes applied, ${runtime.toString().padStart(4, " ")}ms, rule ${rule.getMetadata().key}\n`);
if ((extra === null || extra === void 0 ? void 0 : extra.extraInfo) === true) {
for (const i of applied.appliedIssues) {
process.stderr.write("\t\t" + i.getKey() + ": " + i.getMessage() + "\n");
}
}
}
if (appliedCount > 0) {
changed += appliedCount;
const before = Date.now();
reg.parse();
const runtime = Date.now() - before;
if ((extra === null || extra === void 0 ? void 0 : extra.quiet) !== true) {
process.stderr.write(`\tParse, ${runtime}ms\n`);
}
}
else {
break;
}
}
}
if (changed === 0) {
break;
}
}
this.writeChangesToFS(fs, reg);
this.clearSyntaxCache(reg);
}
///////////////////////////////////////////////////
clearSyntaxCache(reg) {
for (const obj of reg.getObjects()) {
if (obj instanceof core_1.ABAPObject) {
obj.setDirty();
}
}
}
writeChangesToFS(fs, reg) {
for (const filename of this.changedFiles.values()) {
const file = reg.getFileByName(filename);
if (file === undefined) {
continue;
}
fs.writeFileSync(file.getFilename(), file.getRaw());
}
}
possibleOverlap(edit, list) {
// only checks if the edits have changes in the same rows
for (const e of list) {
for (const file1 of Object.keys(e)) {
for (const file2 of Object.keys(edit)) {
if (file1 === file2) {
for (const list1 of e[file1]) {
for (const list2 of edit[file2]) {
if (list2.range.start.getRow() <= list1.range.start.getRow()
&& list2.range.end.getRow() >= list1.range.start.getRow()) {
return true;
}
if (list2.range.start.getRow() <= list1.range.end.getRow()
&& list2.range.end.getRow() >= list1.range.end.getRow()) {
return true;
}
}
}
}
}
}
}
return false;
}
applyList(issues, reg) {
const edits = [];
const appliedIssues = [];
for (const i of issues) {
const edit = i.getDefaultFix();
if (edit === undefined) {
continue;
}
else if (this.possibleOverlap(edit, edits) === true) {
continue;
}
appliedIssues.push(i);
edits.push(edit);
}
/*
if (edits.length > 0) {
console.log("EDITS:");
for (const e of edits) {
for (const filename of Object.keys(e)) {
console.log("\t" + filename);
for (const i of e[filename]) {
console.dir(i.range);
console.dir(i.newText);
}
}
}
}
*/
const changed = core_1.Edits.applyEditList(reg, edits);
for (const filename of changed) {
this.changedFiles.add(filename);
}
return {
files: changed,
appliedIssues: appliedIssues,
};
}
}
exports.ApplyFixes = ApplyFixes;
//# sourceMappingURL=fixes.js.map
/***/ }),
/***/ "./build/src/formatters/_format.js":
/*!*****************************************!*\
!*** ./build/src/formatters/_format.js ***!
\*****************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Formatter = void 0;
const Formatters = __webpack_require__(/*! . */ "./build/src/formatters/index.js");
class Formatter {
static format(issues, format, fileCount) {
// todo, this can be done more generic, move to artifacts.ts?
switch (format) {
case "total":
return new Formatters.Total().output(issues, fileCount);
case "json":
return new Formatters.Json().output(issues, fileCount);
case "junit":
return new Formatters.Junit().output(issues, fileCount);
case "codeframe":
return new Formatters.CodeFrame().output(issues, fileCount);
case "checkstyle":
return new Formatters.Checkstyle().output(issues, fileCount);
case "sonarqube":
return new Formatters.Sonarqube().output(issues, fileCount);
case "codeclimate":
return new Formatters.CodeClimate().output(issues, fileCount);
default:
return new Formatters.Standard().output(issues, fileCount);
}
}
}
exports.Formatter = Formatter;
//# sourceMappingURL=_format.js.map
/***/ }),
/***/ "./build/src/formatters/checkstyle.js":
/*!********************************************!*\
!*** ./build/src/formatters/checkstyle.js ***!
\********************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Checkstyle = void 0;
const xml_js_1 = __webpack_require__(/*! xml-js */ "./node_modules/xml-js/lib/index.js");
class Checkstyle {
output(issues, _fileCount) {
const outputObj = {
_declaration: {
_attributes: {
version: "1.0",
encoding: "UTF-8",
},
},
checkstyle: {
_attributes: {
version: "8.36",
},
file: [],
},
};
if (issues.length > 0) {
for (const issue of issues) {
outputObj.checkstyle.file.push({
_attributes: {
name: issue.getFilename(),
},
error: {
_attributes: {
line: issue.getStart().getRow(),
column: issue.getStart().getCol(),
severity: issue.getSeverity().toString(),
message: issue.getMessage(),
source: "abaplint",
},
},
});
}
}
const xml = (0, xml_js_1.js2xml)(outputObj, { compact: true, spaces: 2 });
return xml;
}
}
exports.Checkstyle = Checkstyle;
//# sourceMappingURL=checkstyle.js.map
/***/ }),
/***/ "./build/src/formatters/codeclimate.js":
/*!*********************************************!*\
!*** ./build/src/formatters/codeclimate.js ***!
\*********************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CodeClimate = void 0;
const node_crypto_1 = __webpack_require__(/*! node:crypto */ "node:crypto");
function md5(content) {
return (0, node_crypto_1.createHash)("md5").update(content).digest("hex");
}
class CodeClimate {
output(issues, _fileCount) {
const out = [];
const defaultSeverity = "info";
const severityArray = ["info", "minor", "major", "critical", "blocker"];
for (const issue of issues) {
const single = {
type: "issue",
check_name: issue.getKey(),
description: issue.getMessage(),
categories: ["Code Quality"],
location: {
path: issue.getFilename(),
lines: {
begin: issue.getStart().getRow(),
end: issue.getEnd().getRow(),
},
},
severity: (severityArray.includes(issue.getSeverity().toLowerCase())) ? issue.getSeverity().toLowerCase() : defaultSeverity,
fingerprint: md5(issue.getKey() + issue.getMessage() + issue.getFilename() + issue.getStart().getRow() + issue.getEnd().getRow()),
};
out.push(single);
}
return JSON.stringify(out) + "\n";
}
}
exports.CodeClimate = CodeClimate;
//# sourceMappingURL=codeclimate.js.map
/***/ }),
/***/ "./build/src/formatters/codeframe.js":
/*!*******************************************!*\
!*** ./build/src/formatters/codeframe.js ***!
\*******************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CodeFrame = void 0;
const total_1 = __webpack_require__(/*! ./total */ "./build/src/formatters/total.js");
const fs = __webpack_require__(/*! fs */ "fs");
const chalk_1 = __webpack_require__(/*! chalk */ "./node_modules/chalk/source/index.js");
function issueSort(a, b) {
return a.filename.localeCompare(b.filename)
|| (a.row - b.row)
|| (a.col - b.col);
}
class CodeFrame {
constructor() {
this.currentFilename = "";
this.currentFileLinesCount = 0;
this.fileContent = [];
}
output(issues, fileCount) {
const builtIssues = this.convertAllIssues(issues).sort(issueSort); // Make sure it is sorted by filename for caching to work
return [
...builtIssues.map(i => this.renderIssue(i)),
(issues.length > 0 ? chalk_1.default.red(new total_1.Total().output(issues, fileCount)) : chalk_1.default.green(new total_1.Total().output(issues, fileCount))),
].join("\n");
}
convertAllIssues(issues) {
return issues.map(i => this.convertIssue(i));
}
cacheFile(filename) {
if (filename !== this.currentFilename) {
this.currentFilename = filename;
this.fileContent = fs.readFileSync(filename, "utf8").split(/\r?\n/);
this.currentFileLinesCount = this.fileContent.length;
}
}
renderIssue(issue) {
this.cacheFile(issue.filename);
const frameSize = 1;
const lineFrom = Math.max(issue.row - frameSize, 1);
const lineTo = Math.min(issue.row + frameSize, this.currentFileLinesCount);
const issueLineIndex = issue.row - 1;
const padSize = Math.ceil(Math.log10(lineTo)) + 4;
const code = [];
for (let lineIndex = lineFrom - 1; lineIndex < lineTo; lineIndex++) {
const prefix = `${ /*(lineIndex === issueLineIndex) ? ">" :*/" "}${lineIndex + 1} |`.padStart(padSize);
code.push(prefix + this.fileContent[lineIndex]);
if (lineIndex === issueLineIndex) {
code.push("|".padStart(padSize) + " ".repeat(issue.col - 1) + "^");
}
}
const severityStr = issue.severity === "E"
? chalk_1.default.red(issue.severity)
: chalk_1.default.yellow(issue.severity);
return `[${severityStr}] ${issue.description} (${issue.issueKey}) @ ${issue.location}`
+ "\n"
+ code.map(str => chalk_1.default.grey(str)).join("\n")
+ "\n";
}
renderLocation(issue) {
return issue.getFilename() + "[" + issue.getStart().getRow() + ", " + issue.getStart().getCol() + "]";
}
convertIssue(issue) {
return {
location: this.renderLocation(issue),
description: issue.getMessage(),
issueKey: issue.getKey(),
filename: issue.getFilename(),
severity: issue.getSeverity().toString().charAt(0), //E/W/I
row: issue.getStart().getRow(),
col: issue.getStart().getCol(),
};
}
}
exports.CodeFrame = CodeFrame;
//# sourceMappingURL=codeframe.js.map
/***/ }),
/***/ "./build/src/formatters/index.js":
/*!***************************************!*\
!*** ./build/src/formatters/index.js ***!
\***************************************/
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
__exportStar(__webpack_require__(/*! ./json */ "./build/src/formatters/json.js"), exports);
__exportStar(__webpack_require__(/*! ./junit */ "./build/src/formatters/junit.js"), exports);
__exportStar(__webpack_require__(/*! ./standard */ "./build/src/formatters/standard.js"), exports);
__exportStar(__webpack_require__(/*! ./total */ "./build/src/formatters/total.js"), exports);
__exportStar(__webpack_require__(/*! ./codeframe */ "./build/src/formatters/codeframe.js"), exports);
__exportStar(__webpack_require__(/*! ./checkstyle */ "./build/src/formatters/checkstyle.js"), exports);
__exportStar(__webpack_require__(/*! ./sonarqube */ "./build/src/formatters/sonarqube.js"), exports);
__exportStar(__webpack_require__(/*! ./codeclimate */ "./build/src/formatters/codeclimate.js"), exports);
//# sourceMappingURL=index.js.map
/***/ }),
/***/ "./build/src/formatters/json.js":
/*!**************************************!*\
!*** ./build/src/formatters/json.js ***!
\**************************************/
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Json = void 0;
class Json {
output(issues, _fileCount) {
const out = [];
for (const issue of issues) {
const single = {
description: issue.getMessage(),
key: issue.getKey(),
file: issue.getFilename(),
start: {
row: issue.getStart().getRow(),
col: issue.getStart().getCol(),
},
end: {
row: issue.getEnd().getRow(),
col: issue.getEnd().getCol(),
},
severity: issue.getSeverity(),
};
out.push(single);
}
return JSON.stringify(out) + "\n";
}
}
exports.Json = Json;
//# sourceMappingURL=json.js.map
/***/ }),
/***/ "./build/src/formatters/junit.js":
/*!***************************************!*\
!*** ./build/src/formatters/junit.js ***!
\***************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Junit = void 0;
const xml_js_1 = __webpack_require__(/*! xml-js */ "./node_modules/xml-js/lib/index.js");
const fs = __webpack_require__(/*! fs */ "fs");
class Junit {
constructor() {
this.currentFilename = "";
this.currentFileLinesCount = 0;
this.fileContent = [];
}
output(issues, _fileCount) {
const outputObj = {
_declaration: {
_attributes: {
version: "1.0",
encoding: "UTF-8",
},
},
testsuites: {
testsuite: {
_attributes: {
name: "abaplint",
tests: issues.length || 1,
failures: issues.length,
errors: 0,
skipped: 0,
},
testcase: [],
},
},
};
if (issues.length > 0) {
for (const issue of issues) {
outputObj.testsuites.testsuite.testcase.push({
_attributes: {
classname: issue.getFilename().split(".")[0],
file: issue.getFilename(),
name: `${issue.getFilename()}: [${issue.getStart().getRow()}, ${issue.getStart().getCol()}] - ${issue.getKey()}`,
},
failure: {
_attributes: {
message: this.formatFailureMessage(issue.getMessage()),
type: issue.getSeverity().toString(),
},
_cdata: `${this.renderIssue(issue)}`,
},
});
}
}
else {
outputObj.testsuites.testsuite.testcase.push({
_attributes: {
classname: "none",
name: "OK",
},
});
}
const xml = (0, xml_js_1.js2xml)(outputObj, { compact: true, spaces: 2 });
return xml;
}
cacheFile(filename) {
if (filename !== this.currentFilename) {
this.currentFilename = filename;
this.fileContent = fs.readFileSync(filename, "utf8").split(/\r?\n/);
this.currentFileLinesCount = this.fileContent.length;
}
}
renderIssue(issue) {
this.cacheFile(issue.getFilename());
const frameSize = 1;
const lineFrom = Math.max(issue.getStart().getRow() - frameSize, 1);
const lineTo = Math.min(issue.getStart().getRow() + frameSize, this.currentFileLinesCount);
const issueLineIndex = issue.getStart().getRow() - 1;
const padSize = Math.ceil(Math.log10(lineTo)) + 4;
const code = [];
for (let lineIndex = lineFrom - 1; lineIndex < lineTo; lineIndex++) {
const prefix = `${lineIndex + 1} | `.padStart(padSize, "0");
code.push(prefix + this.fileContent[lineIndex]);
if (lineIndex === issueLineIndex) {
code.push("| ".padStart(padSize) + " ".repeat(issue.getStart().getCol() - 1) + "^");
}
}
return code.map(str => str).join("\n");
}
formatFailureMessage(message) {
let ret = message;
while (ret.includes("<")) {
ret = ret.replace("<", "<");
}
while (ret.includes(">")) {
ret = ret.replace(">", ">");
}
return ret;
}
}
exports.Junit = Junit;
//# sourceMappingURL=junit.js.map
/***/ }),
/***/ "./build/src/formatters/sonarqube.js":
/*!*******************************************!*\
!*** ./build/src/formatters/sonarqube.js ***!
\*******************************************/
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Sonarqube = void 0;
class Sonarqube {
output(issues, _fileCount) {
const out = {};
const issueArray = [];
const defaultSeverity = "INFO";
const severityArray = ["INFO", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"];
for (const issue of issues) {
const startOffset = issue.getStart().getCol() - 1;
const endOffset = issue.getEnd().getCol() - 2;
const single = {
engineId: "abaplint",
ruleId: issue.getKey(),
severity: (severityArray.includes(issue.getSeverity().toUpperCase())) ? issue.getSeverity().toUpperCase() : defaultSeverity,
type: "CODE_SMELL",
primaryLocation: {
message: issue.getMessage(),
filePath: issue.getFilename(),
textRange: {
startLine: issue.getStart().getRow(),
endLine: issue.getEnd().getRow(),
startColumn: startOffset,
endColumn: endOffset,
},
},
effortMinutes: 10,
};
if (startOffset >= endOffset) {
delete single.primaryLocation.textRange.startColumn;
delete single.primaryLocation.textRange.endColumn;
}
issueArray.push(single);
}
out.issues = issueArray;
return JSON.stringify(out) + "\n";
}
}
exports.Sonarqube = Sonarqube;
//# sourceMappingURL=sonarqube.js.map
/***/ }),
/***/ "./build/src/formatters/standard.js":
/*!******************************************!*\
!*** ./build/src/formatters/standard.js ***!
\******************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Standard = void 0;
const total_1 = __webpack_require__(/*! ./total */ "./build/src/formatters/total.js");
const path = __webpack_require__(/*! node:path */ "node:path");
class Standard {
output(issues, fileCount) {
const tuples = [];
for (const issue of issues) {
tuples.push(this.build(issue));
}
tuples.sort((a, b) => {
const nameCompare = a.rawFilename.localeCompare(b.rawFilename);
if (nameCompare === 0) {
const rowCompare = a.startPos.getRow() - b.startPos.getRow();
if (rowCompare === 0) {
return a.startPos.getCol() - b.startPos.getCol();
}
else {
return rowCompare;
}
}
else {
return nameCompare;
}
});
const result = this.columns(tuples);
return result + new total_1.Total().output(issues, fileCount);
}
columns(issues) {
let max = 0;
issues.forEach((tuple) => { if (max < tuple.filename.length) {
max = tuple.filename.length;
} });
let result = "";
issues.forEach((issue) => {
result = result +
this.pad(issue.filename, max - issue.filename.length) +
issue.description +
` [${issue.severity.charAt(0)}]\n`; //E/W/I
});
return result;
}
pad(input, length) {
let output = input;
for (let i = 0; i < length; i++) {
output = output + " ";
}
return output + " - ";
}
build(issue) {
let filename = path.normalize(issue.getFilename());
if (filename.startsWith("\\\\?\\")) {
// windows UNC path
filename = filename.substring(4);
}
const relativePath = path.relative(process.cwd(), filename);
return {
filename: relativePath + "[" + issue.getStart().getRow() + ", " + issue.getStart().getCol() + "]",
description: issue.getMessage() + " (" + issue.getKey() + ")",
startPos: issue.getStart(),
rawFilename: relativePath,
severity: issue.getSeverity().toString(),
};
}
}
exports.Standard = Standard;
//# sourceMappingURL=standard.js.map
/***/ }),
/***/ "./build/src/formatters/total.js":
/*!***************************************!*\
!*** ./build/src/formatters/total.js ***!
\***************************************/
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Total = void 0;
class Total {
output(issues, fileCount) {
return "abaplint: " + issues.length + " issue(s) found, " + fileCount + " file(s) analyzed\n";
}
}
exports.Total = Total;
//# sourceMappingURL=total.js.map
/***/ }),
/***/ "./build/src/index.js":
/*!****************************!*\
!*** ./build/src/index.js ***!
\****************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.GENERIC_ERROR = void 0;
exports.run = run;
const fs = __webpack_require__(/*! fs */ "fs");
const os = __webpack_require__(/*! os */ "os");
const path = __webpack_require__(/*! path */ "path");
const ProgressBar = __webpack_require__(/*! progress */ "./node_modules/progress/index.js");
const childProcess = __webpack_require__(/*! child_process */ "child_process");
const JSON5 = __webpack_require__(/*! json5 */ "./node_modules/json5/dist/index.mjs");
const core_1 = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
const _format_1 = __webpack_require__(/*! ./formatters/_format */ "./build/src/formatters/_format.js");
const file_operations_1 = __webpack_require__(/*! ./file_operations */ "./build/src/file_operations.js");
const apack_dependency_provider_1 = __webpack_require__(/*! ./apack_dependency_provider */ "./build/src/apack_dependency_provider.js");
const fixes_1 = __webpack_require__(/*! ./fixes */ "./build/src/fixes.js");
const rename_1 = __webpack_require__(/*! ./rename */ "./build/src/rename.js");
exports.GENERIC_ERROR = "generic_error";
class Progress {
set(total, _text) {
this.bar = new ProgressBar(":percent - :elapseds - :text", { total, renderThrottle: 100 });
}
async tick(text) {
this.bar.tick({ text });
this.bar.render();
}
tickSync(text) {
this.bar.tick({ text });
this.bar.render();
}
}
function loadConfig(filename) {
// possible cases:
// a) nothing specified, using abaplint.json from cwd
// b) nothing specified, no abaplint.json in cwd
// c) specified and found
// d) specified and not found => use default
// e) supplied but a directory => use default
let f = "";
if (filename === undefined) {
f = process.cwd() + path.sep + "abaplint.json";
if (fs.existsSync(f) === false) {
f = process.cwd() + path.sep + "abaplint.jsonc";
}
if (fs.existsSync(f) === false) {
f = process.cwd() + path.sep + "abaplint.json5";
}
if (fs.existsSync(f) === false) {
process.stderr.write("Using default config\n");
return { config: core_1.Config.getDefault(), base: "." };
}
}
else {
if (fs.existsSync(filename) === false) {
process.stderr.write("ERROR: Specified abaplint configuration file does not exist, using default config\n");
return { config: core_1.Config.getDefault(), base: "." };
}
else if (fs.statSync(filename).isDirectory() === true) {
process.stderr.write("Supply filename, not directory, using default config\n");
return { config: core_1.Config.getDefault(), base: "." };
}
f = filename;
}
// evil hack to get JSON5 working
if (JSON5.parse === undefined) {
// @ts-ignore
JSON5.parse = JSON5.default.parse;
}
process.stderr.write("Using config: " + f + "\n");
const json = fs.readFileSync(f, "utf8");
const parsed = JSON5.parse(json);
const vers = core_1.Version;
if (Object.keys(core_1.Version).some(v => vers[v] === parsed.syntax.version) === false) {
throw "Error: Unknown version in abaplint.json";
}
return {
config: new core_1.Config(json),
base: path.dirname(f) === process.cwd() ? "." : path.dirname(f),
};
}
async function loadDependencies(config, compress, bar, base) {
let files = [];
const deps = config.get().dependencies || [];
const useApack = config.get().global.useApackDependencies;
if (useApack) {
const apackPath = path.join(base, ".apack-manifest.xml");
if (fs.existsSync(apackPath)) {
const apackManifest = fs.readFileSync(apackPath, "utf8");
deps.push(...apack_dependency_provider_1.ApackDependencyProvider.fromManifest(apackManifest));
}
}
if (!deps) {
return [];
}
for (const d of deps) {
if (d.folder) {
const g = base + d.folder + d.files;
const names = file_operations_1.FileOperations.loadFileNames(g, false);
if (names.length > 0) {
process.stderr.write("Using dependency from: " + g + "\n");
files = files.concat(await file_operations_1.FileOperations.loadFiles(compress, names, bar));
continue;
}
}
if (d.url) {
process.stderr.write("Clone: " + d.url + "\n");
let dir = fs.mkdtempSync(path.join(os.tmpdir(), "abaplint-"));
dir = file_operations_1.FileOperations.toUnixPath(dir);
let branch = "";
if (d.branch) {
branch = "-b " + d.branch + " ";
}
childProcess.execSync("git clone --quiet --depth 1 " + branch + d.url + " .", { cwd: dir, stdio: "inherit" });
const names = file_operations_1.FileOperations.loadFileNames(dir + d.files);
files = files.concat(await file_operations_1.FileOperations.loadFiles(compress, names, bar));
file_operations_1.FileOperations.deleteFolderRecursive(dir);
}
}
return files;
}
function displayHelp() {
// follow https://docopt.org conventions,
return "Usage:\n" +
" abaplint [<abaplint.json> -f <format> -c --outformat <format> --outfile <file> --fix --file <file>] \n" +
" abaplint -h | --help show this help\n" +
" abaplint -v | --version show version\n" +
" abaplint -d | --default show default configuration\n" +
"\n" +
"Options:\n" +
" -f, --format <format> output format (standard, total, json, summary, junit, codeframe, checkstyle)\n" +
" --outformat <format> output format, use in combination with outfile\n" +
" --outfile <file> output issues to file in format\n" +
" --fix apply quick fixes to files\n" +
" --rename rename object according to rules in abaplint.json\n" +
" -p output performance information\n" +
" -c compress files in memory\n" +
" --file input file, glob format\n";
}
function out(issues, length, arg) {
const output = _format_1.Formatter.format(issues, arg.format, length);
if (arg.outFormat && arg.outFile) {
const fileContents = _format_1.Formatter.format(issues, arg.outFormat, length);
fs.writeFileSync(arg.outFile, fileContents, "utf-8");
}
return output;
}
async function run(arg) {
var _a, _b;
// evil hack to get JSON5 working
if (JSON5.parse === undefined) {
// @ts-ignore
JSON5.parse = JSON5.default.parse;
}
if (JSON5.stringify === undefined) {
// @ts-ignore
JSON5.stringify = JSON5.default.stringify;
}
let output = "";
let issues = [];
let reg = undefined;
const progress = new Progress();
if (arg.showHelp === true) {
output = output + displayHelp();
}
else if (arg.showVersion === true) {
output = output + core_1.Registry.abaplintVersion() + "\n";
}
else if (arg.outputDefaultConfig === true) {
output = output + JSON.stringify(core_1.Config.getDefault().get(), undefined, 2) + "\n";
}
else {
process.stderr.write("abaplint " + core_1.Registry.abaplintVersion() + "\n");
let loaded = [];
let deps = [];
const { config, base } = loadConfig(arg.configFilename);
try {
if (config.get().global.files === undefined) {
throw "Error: Update abaplint configuration file to latest format";
}
if (arg.file) {
const files = file_operations_1.FileOperations.loadFileNames(base + arg.file);
loaded = await file_operations_1.FileOperations.loadFiles(arg.compress, files, progress);
}
else {
const configFiles = config.get().global.files;
const filesList = Array.isArray(configFiles) ? configFiles : [configFiles];
for (const l of filesList) {
const files = file_operations_1.FileOperations.loadFileNames(base + l);
const temp = await file_operations_1.FileOperations.loadFiles(arg.compress, files, progress);
if (loaded.length === 0) {
loaded = temp;
}
else {
loaded = loaded.concat(temp);
}
}
}
deps = await loadDependencies(config, arg.compress, progress, base);
reg = new core_1.Registry(config);
reg.addDependencies(deps);
reg.addFiles(loaded); // if the object exists in repo, it should take precedence over deps
await reg.parseAsync({ progress, outputPerformance: arg.performanceInformation });
if (arg.runFix !== true) {
issues = issues.concat(reg.findIssues({ progress, outputPerformance: arg.performanceInformation }));
// require('v8').writeHeapSnapshot();
}
}
catch (error) {
const file = new core_1.MemoryFile("generic", "dummy");
const message = error.toString() + " " + ((_b = (_a = error.stack) === null || _a === void 0 ? void 0 : _a.split("\n")[1]) === null || _b === void 0 ? void 0 : _b.trim());
const issue = core_1.Issue.atPosition(file, new core_1.Position(1, 1), message, exports.GENERIC_ERROR);
issues = [issue];
}
let extra = "";
if (arg.runFix === true && reg) {
await new fixes_1.ApplyFixes().applyFixes(reg, fs, { quiet: false, extraInfo: arg.performanceInformation });
issues = [...reg.findIssues()]; // used in exercism ABAP test runner
extra = "Fixes applied";
}
else if (arg.runRename === true && reg) {
if (issues.length === 0) {
new rename_1.Rename(reg).run(config.get(), base, fs);
extra = "Renames applied";
}
else {
extra = "Renames NOT applied, issues found";
}
}
output = out(issues, loaded.length, arg) + extra;
}
return { output, issues, reg };
}
//# sourceMappingURL=index.js.map
/***/ }),
/***/ "./build/src/rename.js":
/*!*****************************!*\
!*** ./build/src/rename.js ***!
\*****************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Rename = void 0;
const abaplint = __webpack_require__(/*! @abaplint/core */ "./node_modules/@abaplint/core/build/src/index.js");
const path = __webpack_require__(/*! path */ "path");
const file_operations_1 = __webpack_require__(/*! ./file_operations */ "./build/src/file_operations.js");
class Rename {
constructor(reg) {
this.reg = reg;
this.deletedFiles = new Set();
this.addedFiles = new Set();
this.updatedFiles = new Set();
}
run(config, base, fs, quiet) {
const rconfig = config.rename;
if (rconfig === undefined) {
return;
}
this.skip(rconfig);
this.rename(rconfig, quiet);
if (rconfig.output === undefined || rconfig.output === "") {
// write changes inline
this.deletedFiles.forEach(f => {
if (quiet !== true) {
console.log("rm " + f);
}
fs.rmSync(f);
});
this.addedFiles.forEach(f => {
if (quiet !== true) {
console.log("write " + f);
}
const file = this.reg.getFileByName(f);
if (file !== undefined) {
fs.writeFileSync(file.getFilename(), file.getRaw());
}
});
this.updatedFiles.forEach(f => {
if (quiet !== true) {
console.log("update " + f);
}
const file = this.reg.getFileByName(f);
if (file !== undefined) {
fs.writeFileSync(file.getFilename(), file.getRaw());
}
});
}
else {
// output full registry contents to output folder
this.write(rconfig, base, fs);
}
}
////////////////////////
write(rconfig, base, fs) {
const outputFolder = base + path.posix.sep + rconfig.output;
console.log("Base: " + base);
console.log("Output folder: " + outputFolder);
file_operations_1.FileOperations.deleteFolderRecursive(outputFolder);
const myBase = file_operations_1.FileOperations.toUnixPath(path.resolve(base));
for (const o of this.reg.getObjects()) {
if (this.reg.isDependency(o) === true) {
continue;
}
for (const f of o.getFiles()) {
// yea, ffs
const n = outputFolder + f.getFilename().replace(myBase, "").replace("//?/C:", "");
console.log("Write " + n);
fs.mkdirSync(path.dirname(n), { recursive: true });
fs.writeFileSync(n, f.getRaw());
}
}
}
rename(rconfig, quiet) {
const renamer = new abaplint.Rename(this.reg);
for (const o of this.reg.getObjects()) {
if (this.reg.isDependency(o) === true) {
continue;
}
for (const p of rconfig.patterns || []) {
if (!(o.getType().match(p.type))) {
continue;
}
const regex = new RegExp(p.oldName, "i");
const match = regex.exec(o.getName());
if (!match) {
continue;
}
const newStr = o.getName().replace(regex, p.newName);
if (quiet !== true) {
console.log("Renaming " + o.getType() + " " + o.getName().padEnd(30, " ") + " -> " + newStr);
}
const result = renamer.rename(o.getType(), o.getName(), newStr);
result.updatedFiles.forEach(f => { this.updatedFiles.add(f); });
result.deletedFiles.forEach(f => { this.deletedFiles.add(f); });
result.addedFiles.forEach(f => { this.addedFiles.add(f); });
break;
}
}
}
skip(rconfig) {
if (rconfig.skip) {
for (const s of rconfig.skip) {
const all = [];
for (const f of this.reg.getFiles()) {
all.push(f);
}
for (const n of all) {
if (n.getFilename().match(s)) {
console.log(n.getFilename() + " skipped");
this.reg.removeFile(n);
}
}
}
this.reg.parse();
}
}
}
exports.Rename = Rename;
//# sourceMappingURL=rename.js.map
/***/ }),
/***/ "./node_modules/@abaplint/core/build/src/abap/1_lexer/lexer.js":
/*!*********************************************************************!*\
!*** ./node_modules/@abaplint/core/build/src/abap/1_lexer/lexer.js ***!
\*********************************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Lexer = void 0;
const position_1 = __webpack_require__(/*! ../../position */ "./node_modules/@abaplint/core/build/src/position.js");
const virtual_position_1 = __webpack_require__(/*! ../../virtual_position */ "./node_modules/@abaplint/core/build/src/virtual_position.js");
const tokens_1 = __webpack_require__(/*! ./tokens */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/tokens/index.js");
const lexer_buffer_1 = __webpack_require__(/*! ./lexer_buffer */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/lexer_buffer.js");
const lexer_stream_1 = __webpack_require__(/*! ./lexer_stream */ "./node_modules/@abaplint/core/build/src/abap/1_lexer/lexer_stream.js");
class Lexer {
constructor() {
this.ModeNormal = 1;
this.ModePing = 2;
this.ModeStr = 3;
this.ModeTemplate = 4;
this.ModeComment = 5;
this.ModePragma = 6;
}
run(file, virtual) {
this.virtual = virtual;
this.tokens = [];
this.m = this.ModeNormal;
this.process(file.getRaw());
return { file, tokens: this.tokens };
}
add() {
const s = this.buffer.get().trim();
if (s.length > 0) {
const col = this.stream.getCol();
const row = this.stream.getRow();
let whiteBefore = false;
if (this.stream.getOffset() - s.length >= 0) {
const prev = this.stream.getRaw().substr(this.stream.getOffset() - s.length, 1);
if (prev === " " || prev === "\n" || prev === "\t" || prev === ":") {
whiteBefore = true;
}
}
let whiteAfter = false;
const next = this.stream.nextChar();
if (nex