UNPKG

@abaplint/cli

Version:

abaplint - Command Line Interface

1,283 lines (1,216 loc) • 4.48 MB
/******/ (() => { // 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("<", "&lt;"); } while (ret.includes(">")) { ret = ret.replace(">", "&gt;"); } 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