@abaplint/cli 2.82.1 → 2.82.5
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/abaplint +1 -1
- package/build/{bundle.js → cli.js} +18 -7
- package/build/src/apack_dependency_provider.js +30 -0
- package/build/src/compressed_file.js +22 -0
- package/build/src/file_operations.js +48 -0
- package/build/src/fixes.js +74 -0
- package/build/src/formatters/_format.js +23 -0
- package/build/src/formatters/_iformatter.js +3 -0
- package/build/src/formatters/codeframe.js +74 -0
- package/build/src/formatters/index.js +18 -0
- package/build/src/formatters/json.js +28 -0
- package/build/src/formatters/junit.js +58 -0
- package/build/src/formatters/standard.js +61 -0
- package/build/src/formatters/total.js +10 -0
- package/build/src/index.d.ts +20 -0
- package/build/src/index.js +206 -0
- package/build/src/rename.js +79 -0
- package/package.json +6 -4
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileOperations = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const zlib = require("zlib");
|
|
7
|
+
const glob = require("glob");
|
|
8
|
+
const core_1 = require("@abaplint/core");
|
|
9
|
+
const compressed_file_1 = require("./compressed_file");
|
|
10
|
+
class FileOperations {
|
|
11
|
+
static deleteFolderRecursive(dir) {
|
|
12
|
+
if (fs.existsSync(dir) === false) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
fs.rmSync(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
static loadFileNames(arg, error = true) {
|
|
18
|
+
const files = glob.sync(arg, { nosort: true, nodir: true });
|
|
19
|
+
if (files.length === 0 && error) {
|
|
20
|
+
throw "Error: No files found";
|
|
21
|
+
}
|
|
22
|
+
return files;
|
|
23
|
+
}
|
|
24
|
+
static async loadFiles(compress, input, bar) {
|
|
25
|
+
const files = [];
|
|
26
|
+
bar.set(input.length, "Reading files");
|
|
27
|
+
for (const filename of input) {
|
|
28
|
+
bar.tick("Reading files - " + path.basename(filename));
|
|
29
|
+
const base = filename.split("/").reverse()[0];
|
|
30
|
+
if (base.split(".").length <= 2) {
|
|
31
|
+
continue; // not a abapGit file
|
|
32
|
+
}
|
|
33
|
+
// note that readFileSync is typically faster than async readFile,
|
|
34
|
+
// https://medium.com/@adamhooper/node-synchronous-code-runs-faster-than-asynchronous-code-b0553d5cf54e
|
|
35
|
+
const raw = fs.readFileSync(filename, "utf8");
|
|
36
|
+
if (compress === true) {
|
|
37
|
+
// todo, util.promisify(zlib.deflate) does not seem to work?
|
|
38
|
+
files.push(new compressed_file_1.CompressedFile(filename, zlib.deflateSync(raw).toString("base64")));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
files.push(new core_1.MemoryFile(filename, raw));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.FileOperations = FileOperations;
|
|
48
|
+
//# sourceMappingURL=file_operations.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyFixes = void 0;
|
|
4
|
+
const core_1 = require("@abaplint/core");
|
|
5
|
+
function applyFixes(inputIssues, reg, fs, bar) {
|
|
6
|
+
let changed = [];
|
|
7
|
+
let iteration = 1;
|
|
8
|
+
let issues = inputIssues;
|
|
9
|
+
const MAX_ITERATIONS = 30;
|
|
10
|
+
bar === null || bar === void 0 ? void 0 : bar.set(MAX_ITERATIONS, "Apply Fixes");
|
|
11
|
+
while (iteration <= MAX_ITERATIONS) {
|
|
12
|
+
bar === null || bar === void 0 ? void 0 : bar.tick("Apply Fixes, iteration " + iteration + ", " + issues.length + " candidates");
|
|
13
|
+
changed = applyList(issues, reg, fs);
|
|
14
|
+
if (changed.length === 0) {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
iteration++;
|
|
18
|
+
issues = reg.parse().findIssues();
|
|
19
|
+
}
|
|
20
|
+
// always end the progress indicator at 100%
|
|
21
|
+
while (iteration <= MAX_ITERATIONS) {
|
|
22
|
+
bar === null || bar === void 0 ? void 0 : bar.tick("Apply Fixes, iteration " + iteration + ", " + issues.length + " candidates");
|
|
23
|
+
iteration++;
|
|
24
|
+
}
|
|
25
|
+
return issues;
|
|
26
|
+
}
|
|
27
|
+
exports.applyFixes = applyFixes;
|
|
28
|
+
function possibleOverlap(edit, list) {
|
|
29
|
+
// only checks if the edits have changes in the same rows
|
|
30
|
+
for (const e of list) {
|
|
31
|
+
for (const file1 of Object.keys(e)) {
|
|
32
|
+
for (const file2 of Object.keys(edit)) {
|
|
33
|
+
if (file1 === file2) {
|
|
34
|
+
for (const list1 of e[file1]) {
|
|
35
|
+
for (const list2 of edit[file2]) {
|
|
36
|
+
if (list2.range.start.getRow() <= list1.range.start.getRow()
|
|
37
|
+
&& list2.range.end.getRow() >= list1.range.start.getRow()) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (list2.range.start.getRow() <= list1.range.start.getRow()
|
|
41
|
+
&& list2.range.end.getRow() >= list1.range.end.getRow()) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
function applyList(issues, reg, fs) {
|
|
53
|
+
const edits = [];
|
|
54
|
+
for (const i of issues) {
|
|
55
|
+
const edit = i.getFix();
|
|
56
|
+
if (edit === undefined) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
else if (possibleOverlap(edit, edits) === true) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
edits.push(edit);
|
|
63
|
+
}
|
|
64
|
+
const changed = (0, core_1.applyEditList)(reg, edits);
|
|
65
|
+
for (const filename of changed) {
|
|
66
|
+
const file = reg.getFileByName(filename);
|
|
67
|
+
if (file === undefined) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
fs.writeFileSync(file.getFilename(), file.getRaw());
|
|
71
|
+
}
|
|
72
|
+
return changed;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=fixes.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Formatter = void 0;
|
|
4
|
+
const Formatters = require(".");
|
|
5
|
+
class Formatter {
|
|
6
|
+
static format(issues, format, fileCount) {
|
|
7
|
+
// todo, this can be done more generic, move to artifacts.ts?
|
|
8
|
+
switch (format) {
|
|
9
|
+
case "total":
|
|
10
|
+
return new Formatters.Total().output(issues, fileCount);
|
|
11
|
+
case "json":
|
|
12
|
+
return new Formatters.Json().output(issues, fileCount);
|
|
13
|
+
case "junit":
|
|
14
|
+
return new Formatters.Junit().output(issues, fileCount);
|
|
15
|
+
case "codeframe":
|
|
16
|
+
return new Formatters.CodeFrame().output(issues, fileCount);
|
|
17
|
+
default:
|
|
18
|
+
return new Formatters.Standard().output(issues, fileCount);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.Formatter = Formatter;
|
|
23
|
+
//# sourceMappingURL=_format.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CodeFrame = void 0;
|
|
4
|
+
const total_1 = require("./total");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const chalk_1 = require("chalk");
|
|
7
|
+
function issueSort(a, b) {
|
|
8
|
+
return a.filename.localeCompare(b.filename)
|
|
9
|
+
|| (a.row - b.row)
|
|
10
|
+
|| (a.col - b.col);
|
|
11
|
+
}
|
|
12
|
+
class CodeFrame {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.currentFilename = "";
|
|
15
|
+
this.currentFileLinesCount = 0;
|
|
16
|
+
this.fileContent = [];
|
|
17
|
+
}
|
|
18
|
+
output(issues, fileCount) {
|
|
19
|
+
const builtIssues = this.convertAllIssues(issues).sort(issueSort); // Make sure it is sorted by filename for caching to work
|
|
20
|
+
return [
|
|
21
|
+
...builtIssues.map(i => this.renderIssue(i)),
|
|
22
|
+
(issues.length > 0 ? (0, chalk_1.red)(new total_1.Total().output(issues, fileCount)) : (0, chalk_1.green)(new total_1.Total().output(issues, fileCount))),
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
convertAllIssues(issues) {
|
|
26
|
+
return issues.map(i => this.convertIssue(i));
|
|
27
|
+
}
|
|
28
|
+
cacheFile(filename) {
|
|
29
|
+
if (filename !== this.currentFilename) {
|
|
30
|
+
this.currentFilename = filename;
|
|
31
|
+
this.fileContent = fs.readFileSync(filename, "utf8").split(/\r?\n/);
|
|
32
|
+
this.currentFileLinesCount = this.fileContent.length;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
renderIssue(issue) {
|
|
36
|
+
this.cacheFile(issue.filename);
|
|
37
|
+
const frameSize = 1;
|
|
38
|
+
const lineFrom = Math.max(issue.row - frameSize, 1);
|
|
39
|
+
const lineTo = Math.min(issue.row + frameSize, this.currentFileLinesCount);
|
|
40
|
+
const issueLineIndex = issue.row - 1;
|
|
41
|
+
const padSize = Math.ceil(Math.log10(lineTo)) + 4;
|
|
42
|
+
const code = [];
|
|
43
|
+
for (let lineIndex = lineFrom - 1; lineIndex < lineTo; lineIndex++) {
|
|
44
|
+
const prefix = `${ /*(lineIndex === issueLineIndex) ? ">" :*/" "}${lineIndex + 1} |`.padStart(padSize);
|
|
45
|
+
code.push(prefix + this.fileContent[lineIndex]);
|
|
46
|
+
if (lineIndex === issueLineIndex) {
|
|
47
|
+
code.push("|".padStart(padSize) + " ".repeat(issue.col - 1) + "^");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const severityStr = issue.severity === "E"
|
|
51
|
+
? (0, chalk_1.red)(issue.severity)
|
|
52
|
+
: (0, chalk_1.yellow)(issue.severity);
|
|
53
|
+
return `[${severityStr}] ${issue.description} (${issue.issueKey}) @ ${issue.location}`
|
|
54
|
+
+ "\n"
|
|
55
|
+
+ code.map(str => (0, chalk_1.grey)(str)).join("\n")
|
|
56
|
+
+ "\n";
|
|
57
|
+
}
|
|
58
|
+
renderLocation(issue) {
|
|
59
|
+
return issue.getFilename() + "[" + issue.getStart().getRow() + ", " + issue.getStart().getCol() + "]";
|
|
60
|
+
}
|
|
61
|
+
convertIssue(issue) {
|
|
62
|
+
return {
|
|
63
|
+
location: this.renderLocation(issue),
|
|
64
|
+
description: issue.getMessage(),
|
|
65
|
+
issueKey: issue.getKey(),
|
|
66
|
+
filename: issue.getFilename(),
|
|
67
|
+
severity: issue.getSeverity().toString().charAt(0),
|
|
68
|
+
row: issue.getStart().getRow(),
|
|
69
|
+
col: issue.getStart().getCol(),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.CodeFrame = CodeFrame;
|
|
74
|
+
//# sourceMappingURL=codeframe.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./json"), exports);
|
|
14
|
+
__exportStar(require("./junit"), exports);
|
|
15
|
+
__exportStar(require("./standard"), exports);
|
|
16
|
+
__exportStar(require("./total"), exports);
|
|
17
|
+
__exportStar(require("./codeframe"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Json = void 0;
|
|
4
|
+
class Json {
|
|
5
|
+
output(issues, _fileCount) {
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const issue of issues) {
|
|
8
|
+
const single = {
|
|
9
|
+
description: issue.getMessage(),
|
|
10
|
+
key: issue.getKey(),
|
|
11
|
+
file: issue.getFilename(),
|
|
12
|
+
start: {
|
|
13
|
+
row: issue.getStart().getRow(),
|
|
14
|
+
col: issue.getStart().getCol(),
|
|
15
|
+
},
|
|
16
|
+
end: {
|
|
17
|
+
row: issue.getEnd().getRow(),
|
|
18
|
+
col: issue.getEnd().getCol(),
|
|
19
|
+
},
|
|
20
|
+
severity: issue.getSeverity(),
|
|
21
|
+
};
|
|
22
|
+
out.push(single);
|
|
23
|
+
}
|
|
24
|
+
return JSON.stringify(out) + "\n";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.Json = Json;
|
|
28
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Junit = void 0;
|
|
4
|
+
const xml_js_1 = require("xml-js");
|
|
5
|
+
class Junit {
|
|
6
|
+
output(issues, _fileCount) {
|
|
7
|
+
const outputObj = {
|
|
8
|
+
_declaration: {
|
|
9
|
+
_attributes: {
|
|
10
|
+
version: "1.0",
|
|
11
|
+
encoding: "UTF-8",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
testsuites: {
|
|
15
|
+
testsuite: {
|
|
16
|
+
_attributes: {
|
|
17
|
+
name: "abaplint",
|
|
18
|
+
tests: issues.length || 1,
|
|
19
|
+
failures: issues.length,
|
|
20
|
+
errors: 0,
|
|
21
|
+
skipped: 0,
|
|
22
|
+
},
|
|
23
|
+
testcase: [],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
if (issues.length > 0) {
|
|
28
|
+
for (const issue of issues) {
|
|
29
|
+
outputObj.testsuites.testsuite.testcase.push({
|
|
30
|
+
_attributes: {
|
|
31
|
+
classname: issue.getFilename().split(".")[0],
|
|
32
|
+
file: issue.getFilename(),
|
|
33
|
+
name: `${issue.getFilename()}: [${issue.getStart().getRow()}, ${issue.getStart().getCol()}] - ${issue.getKey()}`,
|
|
34
|
+
},
|
|
35
|
+
failure: {
|
|
36
|
+
_attributes: {
|
|
37
|
+
message: issue.getKey(),
|
|
38
|
+
type: issue.getSeverity().toString(),
|
|
39
|
+
},
|
|
40
|
+
_cdata: `${issue.getMessage()}`,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
outputObj.testsuites.testsuite.testcase.push({
|
|
47
|
+
_attributes: {
|
|
48
|
+
classname: "none",
|
|
49
|
+
name: "OK",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const xml = (0, xml_js_1.js2xml)(outputObj, { compact: true, spaces: 2 });
|
|
54
|
+
return xml;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.Junit = Junit;
|
|
58
|
+
//# sourceMappingURL=junit.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Standard = void 0;
|
|
4
|
+
const total_1 = require("./total");
|
|
5
|
+
class Standard {
|
|
6
|
+
output(issues, fileCount) {
|
|
7
|
+
const tuples = [];
|
|
8
|
+
for (const issue of issues) {
|
|
9
|
+
tuples.push(this.build(issue));
|
|
10
|
+
}
|
|
11
|
+
tuples.sort((a, b) => {
|
|
12
|
+
const nameCompare = a.rawFilename.localeCompare(b.rawFilename);
|
|
13
|
+
if (nameCompare === 0) {
|
|
14
|
+
const rowCompare = a.startPos.getRow() - b.startPos.getRow();
|
|
15
|
+
if (rowCompare === 0) {
|
|
16
|
+
return a.startPos.getCol() - b.startPos.getCol();
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
return rowCompare;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return nameCompare;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const result = this.columns(tuples);
|
|
27
|
+
return result + new total_1.Total().output(issues, fileCount);
|
|
28
|
+
}
|
|
29
|
+
columns(issues) {
|
|
30
|
+
let max = 0;
|
|
31
|
+
issues.forEach((tuple) => { if (max < tuple.filename.length) {
|
|
32
|
+
max = tuple.filename.length;
|
|
33
|
+
} });
|
|
34
|
+
let result = "";
|
|
35
|
+
issues.forEach((issue) => {
|
|
36
|
+
result = result +
|
|
37
|
+
this.pad(issue.filename, max - issue.filename.length) +
|
|
38
|
+
issue.description +
|
|
39
|
+
` [${issue.severity.charAt(0)}]\n`; //E/W/I
|
|
40
|
+
});
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
pad(input, length) {
|
|
44
|
+
let output = input;
|
|
45
|
+
for (let i = 0; i < length; i++) {
|
|
46
|
+
output = output + " ";
|
|
47
|
+
}
|
|
48
|
+
return output + " - ";
|
|
49
|
+
}
|
|
50
|
+
build(issue) {
|
|
51
|
+
return {
|
|
52
|
+
filename: issue.getFilename() + "[" + issue.getStart().getRow() + ", " + issue.getStart().getCol() + "]",
|
|
53
|
+
description: issue.getMessage() + " (" + issue.getKey() + ")",
|
|
54
|
+
startPos: issue.getStart(),
|
|
55
|
+
rawFilename: issue.getFilename(),
|
|
56
|
+
severity: issue.getSeverity().toString(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.Standard = Standard;
|
|
61
|
+
//# sourceMappingURL=standard.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Total = void 0;
|
|
4
|
+
class Total {
|
|
5
|
+
output(issues, fileCount) {
|
|
6
|
+
return "abaplint: " + issues.length + " issue(s) found, " + fileCount + " file(s) analyzed\n";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.Total = Total;
|
|
10
|
+
//# sourceMappingURL=total.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Issue, IRegistry } from "@abaplint/core";
|
|
2
|
+
export declare const GENERIC_ERROR = "generic_error";
|
|
3
|
+
export declare type Arguments = {
|
|
4
|
+
configFilename?: string;
|
|
5
|
+
format: string;
|
|
6
|
+
compress?: boolean;
|
|
7
|
+
parsingPerformance?: boolean;
|
|
8
|
+
showHelp?: boolean;
|
|
9
|
+
showVersion?: boolean;
|
|
10
|
+
outputDefaultConfig?: boolean;
|
|
11
|
+
runFix?: boolean;
|
|
12
|
+
runRename?: boolean;
|
|
13
|
+
outFormat?: string;
|
|
14
|
+
outFile?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function run(arg: Arguments): Promise<{
|
|
17
|
+
output: string;
|
|
18
|
+
issues: Issue[];
|
|
19
|
+
reg: IRegistry | undefined;
|
|
20
|
+
}>;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.run = exports.GENERIC_ERROR = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const ProgressBar = require("progress");
|
|
8
|
+
const childProcess = require("child_process");
|
|
9
|
+
const JSON5 = require("json5");
|
|
10
|
+
const core_1 = require("@abaplint/core");
|
|
11
|
+
const _format_1 = require("./formatters/_format");
|
|
12
|
+
const file_operations_1 = require("./file_operations");
|
|
13
|
+
const apack_dependency_provider_1 = require("./apack_dependency_provider");
|
|
14
|
+
const fixes_1 = require("./fixes");
|
|
15
|
+
const rename_1 = require("./rename");
|
|
16
|
+
exports.GENERIC_ERROR = "generic_error";
|
|
17
|
+
class Progress {
|
|
18
|
+
set(total, _text) {
|
|
19
|
+
this.bar = new ProgressBar(":percent - :elapseds - :text", { total, renderThrottle: 100 });
|
|
20
|
+
}
|
|
21
|
+
async tick(text) {
|
|
22
|
+
this.bar.tick({ text });
|
|
23
|
+
this.bar.render();
|
|
24
|
+
}
|
|
25
|
+
tickSync(text) {
|
|
26
|
+
this.bar.tick({ text });
|
|
27
|
+
this.bar.render();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function loadConfig(filename) {
|
|
31
|
+
// possible cases:
|
|
32
|
+
// a) nothing specified, using abaplint.json from cwd
|
|
33
|
+
// b) nothing specified, no abaplint.json in cwd
|
|
34
|
+
// c) specified and found
|
|
35
|
+
// d) specified and not found => use default
|
|
36
|
+
// e) supplied but a directory => use default
|
|
37
|
+
let f = "";
|
|
38
|
+
if (filename === undefined) {
|
|
39
|
+
f = process.cwd() + path.sep + "abaplint.json";
|
|
40
|
+
if (fs.existsSync(f) === false) {
|
|
41
|
+
f = process.cwd() + path.sep + "abaplint.jsonc";
|
|
42
|
+
}
|
|
43
|
+
if (fs.existsSync(f) === false) {
|
|
44
|
+
f = process.cwd() + path.sep + "abaplint.json5";
|
|
45
|
+
}
|
|
46
|
+
if (fs.existsSync(f) === false) {
|
|
47
|
+
process.stderr.write("Using default config\n");
|
|
48
|
+
return { config: core_1.Config.getDefault(), base: "." };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
if (fs.existsSync(filename) === false) {
|
|
53
|
+
process.stderr.write("ERROR: Specified abaplint configuration file does not exist, using default config\n");
|
|
54
|
+
return { config: core_1.Config.getDefault(), base: "." };
|
|
55
|
+
}
|
|
56
|
+
else if (fs.statSync(filename).isDirectory() === true) {
|
|
57
|
+
process.stderr.write("Supply filename, not directory, using default config\n");
|
|
58
|
+
return { config: core_1.Config.getDefault(), base: "." };
|
|
59
|
+
}
|
|
60
|
+
f = filename;
|
|
61
|
+
}
|
|
62
|
+
// evil hack to get JSON5 working
|
|
63
|
+
if (JSON5.parse === undefined) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
JSON5.parse = JSON5.default.parse;
|
|
66
|
+
}
|
|
67
|
+
process.stderr.write("Using config: " + f + "\n");
|
|
68
|
+
const json = fs.readFileSync(f, "utf8");
|
|
69
|
+
const parsed = JSON5.parse(json);
|
|
70
|
+
const vers = core_1.Version;
|
|
71
|
+
if (Object.keys(core_1.Version).some(v => vers[v] === parsed.syntax.version) === false) {
|
|
72
|
+
throw "Error: Unknown version in abaplint.json";
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
config: new core_1.Config(json),
|
|
76
|
+
base: path.dirname(f) === process.cwd() ? "." : path.dirname(f),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function loadDependencies(config, compress, bar, base) {
|
|
80
|
+
let files = [];
|
|
81
|
+
const deps = config.get().dependencies || [];
|
|
82
|
+
const useApack = config.get().global.useApackDependencies;
|
|
83
|
+
if (useApack) {
|
|
84
|
+
const apackPath = path.join(base, ".apack-manifest.xml");
|
|
85
|
+
if (fs.existsSync(apackPath)) {
|
|
86
|
+
const apackManifest = fs.readFileSync(apackPath, "utf8");
|
|
87
|
+
deps.push(...apack_dependency_provider_1.ApackDependencyProvider.fromManifest(apackManifest));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!deps) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
for (const d of deps) {
|
|
94
|
+
if (d.folder) {
|
|
95
|
+
const g = base + d.folder + d.files;
|
|
96
|
+
const names = file_operations_1.FileOperations.loadFileNames(g, false);
|
|
97
|
+
if (names.length > 0) {
|
|
98
|
+
process.stderr.write("Using dependency from: " + g + "\n");
|
|
99
|
+
files = files.concat(await file_operations_1.FileOperations.loadFiles(compress, names, bar));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (d.url) {
|
|
104
|
+
process.stderr.write("Clone: " + d.url + "\n");
|
|
105
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "abaplint-"));
|
|
106
|
+
childProcess.execSync("git clone --quiet --depth 1 " + d.url + " .", { cwd: dir, stdio: "inherit" });
|
|
107
|
+
const names = file_operations_1.FileOperations.loadFileNames(dir + d.files);
|
|
108
|
+
files = files.concat(await file_operations_1.FileOperations.loadFiles(compress, names, bar));
|
|
109
|
+
file_operations_1.FileOperations.deleteFolderRecursive(dir);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return files;
|
|
113
|
+
}
|
|
114
|
+
function displayHelp() {
|
|
115
|
+
// follow https://docopt.org conventions,
|
|
116
|
+
return "Usage:\n" +
|
|
117
|
+
" abaplint [<abaplint.json> -f <format> -c --outformat <format> --outfile <file> --fix] \n" +
|
|
118
|
+
" abaplint -h | --help show this help\n" +
|
|
119
|
+
" abaplint -v | --version show version\n" +
|
|
120
|
+
" abaplint -d | --default show default configuration\n" +
|
|
121
|
+
"\n" +
|
|
122
|
+
"Options:\n" +
|
|
123
|
+
" -f, --format <format> output format (standard, total, json, summary, junit, codeframe)\n" +
|
|
124
|
+
" --outformat <format> output format, use in combination with outfile\n" +
|
|
125
|
+
" --outfile <file> output issues to file in format\n" +
|
|
126
|
+
" --fix apply quick fixes to files\n" +
|
|
127
|
+
" --rename rename object according to rules in abaplint.json\n" +
|
|
128
|
+
" -p output performance information\n" +
|
|
129
|
+
" -c compress files in memory\n";
|
|
130
|
+
}
|
|
131
|
+
function out(issues, length, arg) {
|
|
132
|
+
const output = _format_1.Formatter.format(issues, arg.format, length);
|
|
133
|
+
if (arg.outFormat && arg.outFile) {
|
|
134
|
+
const fileContents = _format_1.Formatter.format(issues, arg.outFile, length);
|
|
135
|
+
fs.writeFileSync(arg.outFile, fileContents, "utf-8");
|
|
136
|
+
}
|
|
137
|
+
return output;
|
|
138
|
+
}
|
|
139
|
+
async function run(arg) {
|
|
140
|
+
var _a, _b;
|
|
141
|
+
// evil hack to get JSON5 working
|
|
142
|
+
if (JSON5.parse === undefined) {
|
|
143
|
+
// @ts-ignore
|
|
144
|
+
JSON5.parse = JSON5.default.parse;
|
|
145
|
+
}
|
|
146
|
+
if (JSON5.stringify === undefined) {
|
|
147
|
+
// @ts-ignore
|
|
148
|
+
JSON5.stringify = JSON5.default.stringify;
|
|
149
|
+
}
|
|
150
|
+
let output = "";
|
|
151
|
+
let issues = [];
|
|
152
|
+
let reg = undefined;
|
|
153
|
+
const progress = new Progress();
|
|
154
|
+
if (arg.showHelp === true) {
|
|
155
|
+
output = output + displayHelp();
|
|
156
|
+
}
|
|
157
|
+
else if (arg.showVersion === true) {
|
|
158
|
+
output = output + core_1.Registry.abaplintVersion() + "\n";
|
|
159
|
+
}
|
|
160
|
+
else if (arg.outputDefaultConfig === true) {
|
|
161
|
+
output = output + JSON.stringify(core_1.Config.getDefault().get(), undefined, 2) + "\n";
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
process.stderr.write("abaplint " + core_1.Registry.abaplintVersion() + "\n");
|
|
165
|
+
let loaded = [];
|
|
166
|
+
let deps = [];
|
|
167
|
+
const { config, base } = loadConfig(arg.configFilename);
|
|
168
|
+
try {
|
|
169
|
+
if (config.get().global.files === undefined) {
|
|
170
|
+
throw "Error: Update abaplint configuration file to latest format";
|
|
171
|
+
}
|
|
172
|
+
const files = file_operations_1.FileOperations.loadFileNames(base + config.get().global.files);
|
|
173
|
+
loaded = await file_operations_1.FileOperations.loadFiles(arg.compress, files, progress);
|
|
174
|
+
deps = await loadDependencies(config, arg.compress, progress, base);
|
|
175
|
+
reg = new core_1.Registry(config);
|
|
176
|
+
reg.addDependencies(deps);
|
|
177
|
+
reg.addFiles(loaded); // if the object exists in repo, it should take precedence over deps
|
|
178
|
+
await reg.parseAsync({ progress, outputPerformance: arg.parsingPerformance });
|
|
179
|
+
issues = issues.concat(reg.findIssues({ progress, outputPerformance: arg.parsingPerformance }));
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
const file = new core_1.MemoryFile("generic", "dummy");
|
|
183
|
+
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());
|
|
184
|
+
const issue = core_1.Issue.atPosition(file, new core_1.Position(1, 1), message, exports.GENERIC_ERROR);
|
|
185
|
+
issues = [issue];
|
|
186
|
+
}
|
|
187
|
+
let extra = "";
|
|
188
|
+
if (arg.runFix === true && reg) {
|
|
189
|
+
issues = (0, fixes_1.applyFixes)(issues, reg, fs, progress);
|
|
190
|
+
extra = "Fixes applied";
|
|
191
|
+
}
|
|
192
|
+
else if (arg.runRename === true && reg) {
|
|
193
|
+
if (issues.length === 0) {
|
|
194
|
+
new rename_1.Rename(reg).run(config, base);
|
|
195
|
+
extra = "Renames applied";
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
extra = "Renames NOT applied, issues found";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
output = out(issues, loaded.length, arg) + extra;
|
|
202
|
+
}
|
|
203
|
+
return { output, issues, reg };
|
|
204
|
+
}
|
|
205
|
+
exports.run = run;
|
|
206
|
+
//# sourceMappingURL=index.js.map
|