@sasjs/cli 6.5.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +2 -1
  2. package/build/commands/build/build.js +66 -29
  3. package/build/commands/build/spec/build.spec.js +31 -1
  4. package/build/commands/compile/compile.js +168 -33
  5. package/build/commands/compile/compileCommand.js +1 -1
  6. package/build/commands/compile/internal/checkCompileStatus.js +30 -11
  7. package/build/commands/compile/internal/compileFile.js +16 -7
  8. package/build/commands/compile/internal/compileManifest.d.ts +36 -0
  9. package/build/commands/compile/internal/compileManifest.js +610 -0
  10. package/build/commands/compile/internal/index.d.ts +2 -0
  11. package/build/commands/compile/internal/index.js +2 -0
  12. package/build/commands/compile/internal/passThrough.d.ts +12 -0
  13. package/build/commands/compile/internal/passThrough.js +17 -0
  14. package/build/commands/compile/internal/spec/compileTestFile.spec.js +9 -6
  15. package/build/commands/compile/spec/checkCompileStatus.spec.d.ts +1 -0
  16. package/build/commands/compile/spec/checkCompileStatus.spec.js +221 -0
  17. package/build/commands/compile/spec/compileCommand.spec.js +9 -2
  18. package/build/commands/compile/spec/compileManifest.spec.d.ts +1 -0
  19. package/build/commands/compile/spec/compileManifest.spec.js +401 -0
  20. package/build/commands/compile/spec/passThrough.spec.d.ts +1 -0
  21. package/build/commands/compile/spec/passThrough.spec.js +160 -0
  22. package/build/commands/deploy/spec/cbd.spec.js +11 -7
  23. package/build/commands/help/help.js +10 -0
  24. package/build/commands/index.d.ts +1 -0
  25. package/build/commands/index.js +3 -1
  26. package/build/commands/logcheck/collectLogs.d.ts +60 -0
  27. package/build/commands/logcheck/collectLogs.js +386 -0
  28. package/build/commands/logcheck/logCheckConfig.d.ts +72 -0
  29. package/build/commands/logcheck/logCheckConfig.js +283 -0
  30. package/build/commands/logcheck/logcheckCommand.d.ts +6 -0
  31. package/build/commands/logcheck/logcheckCommand.js +200 -0
  32. package/build/commands/logcheck/processLogCheck.d.ts +44 -0
  33. package/build/commands/logcheck/processLogCheck.js +242 -0
  34. package/build/commands/logcheck/renderLogCheck.d.ts +128 -0
  35. package/build/commands/logcheck/renderLogCheck.js +423 -0
  36. package/build/commands/logcheck/spec/collectLogs.spec.d.ts +1 -0
  37. package/build/commands/logcheck/spec/collectLogs.spec.js +477 -0
  38. package/build/commands/logcheck/spec/logCheckConfig.spec.d.ts +1 -0
  39. package/build/commands/logcheck/spec/logCheckConfig.spec.js +323 -0
  40. package/build/commands/logcheck/spec/logcheckCommand.spec.d.ts +1 -0
  41. package/build/commands/logcheck/spec/logcheckCommand.spec.js +360 -0
  42. package/build/commands/logcheck/spec/processLogCheck.spec.d.ts +1 -0
  43. package/build/commands/logcheck/spec/processLogCheck.spec.js +354 -0
  44. package/build/commands/logcheck/spec/renderLogCheck.spec.d.ts +1 -0
  45. package/build/commands/logcheck/spec/renderLogCheck.spec.js +426 -0
  46. package/build/commands/logcheck/types.d.ts +98 -0
  47. package/build/commands/logcheck/types.js +14 -0
  48. package/build/commands/testing/spec/testing.spec.js +8 -2
  49. package/build/types/command/commandAliases.js +1 -0
  50. package/build/types/command/commandFactory.js +2 -0
  51. package/build/types/command/returnCode.d.ts +2 -1
  52. package/build/types/command/returnCode.js +4 -0
  53. package/package.json +3 -3
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __values = (this && this.__values) || function(o) {
50
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
51
+ if (m) return m.call(o);
52
+ if (o && typeof o.length === "number") return {
53
+ next: function () {
54
+ if (o && i >= o.length) o = void 0;
55
+ return { value: o && o[i++], done: !o };
56
+ }
57
+ };
58
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
59
+ };
60
+ var __read = (this && this.__read) || function (o, n) {
61
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
62
+ if (!m) return o;
63
+ var i = m.call(o), r, ar = [], e;
64
+ try {
65
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
66
+ }
67
+ catch (error) { e = { error: error }; }
68
+ finally {
69
+ try {
70
+ if (r && !r.done && (m = i["return"])) m.call(i);
71
+ }
72
+ finally { if (e) throw e.error; }
73
+ }
74
+ return ar;
75
+ };
76
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
77
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
78
+ if (ar || !(i in from)) {
79
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
80
+ ar[i] = from[i];
81
+ }
82
+ }
83
+ return to.concat(ar || Array.prototype.slice.call(from));
84
+ };
85
+ var __importDefault = (this && this.__importDefault) || function (mod) {
86
+ return (mod && mod.__esModule) ? mod : { "default": mod };
87
+ };
88
+ Object.defineProperty(exports, "__esModule", { value: true });
89
+ exports.describeFailure = exports.sumTotals = exports.defaultInputPath = exports.processLogCheck = void 0;
90
+ var path_1 = __importDefault(require("path"));
91
+ var collectLogs_1 = require("./collectLogs");
92
+ var logCheckConfig_1 = require("./logCheckConfig");
93
+ var types_1 = require("./types");
94
+ /**
95
+ * Runs the check: expands the input, parses every log, applies the policy and
96
+ * says whether the run passes.
97
+ *
98
+ * The report is built here rather than in the renderer so that the three
99
+ * output formats cannot disagree about what was found - the summary is the
100
+ * single answer, and human, JSON and CI annotations are three ways of printing
101
+ * it.
102
+ */
103
+ var processLogCheck = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
104
+ var inputPaths, searched, _c, files, missing, unreadable, skipped, toScan, files_1, files_1_1, file, displayPath, logs, scanned, totals;
105
+ var e_1, _d;
106
+ var _e = _b.paths, paths = _e === void 0 ? [] : _e, config = _b.config;
107
+ return __generator(this, function (_f) {
108
+ switch (_f.label) {
109
+ case 0:
110
+ inputPaths = paths.length ? paths : [(0, exports.defaultInputPath)(config)];
111
+ searched = inputPaths.map(function (inputPath) {
112
+ return (0, collectLogs_1.toDisplayPath)(path_1.default.resolve(inputPath));
113
+ });
114
+ return [4 /*yield*/, (0, collectLogs_1.collectLogFiles)(inputPaths)];
115
+ case 1:
116
+ _c = _f.sent(), files = _c.files, missing = _c.missing, unreadable = _c.unreadable;
117
+ skipped = [];
118
+ toScan = [];
119
+ try {
120
+ for (files_1 = __values(files), files_1_1 = files_1.next(); !files_1_1.done; files_1_1 = files_1.next()) {
121
+ file = files_1_1.value;
122
+ displayPath = (0, collectLogs_1.toDisplayPath)(file);
123
+ if ((0, logCheckConfig_1.isIgnoredPath)(displayPath, config.ignorePaths)) {
124
+ skipped.push({ displayPath: displayPath, reason: 'excluded by ignorePaths' });
125
+ continue;
126
+ }
127
+ toScan.push(file);
128
+ }
129
+ }
130
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
131
+ finally {
132
+ try {
133
+ if (files_1_1 && !files_1_1.done && (_d = files_1.return)) _d.call(files_1);
134
+ }
135
+ finally { if (e_1) throw e_1.error; }
136
+ }
137
+ return [4 /*yield*/, Promise.all(toScan.map(function (file) { return (0, collectLogs_1.scanLogFile)(file, config); }))];
138
+ case 2:
139
+ logs = _f.sent();
140
+ scanned = logs.filter(function (log) { return !log.skippedReason; });
141
+ skipped.push.apply(skipped, __spreadArray([], __read(logs.filter(function (log) { return log.skippedReason; }).map(function (log) { return (0, collectLogs_1.toSkippedFile)(log); })), false));
142
+ totals = (0, exports.sumTotals)(scanned, files.length);
143
+ return [2 /*return*/, {
144
+ logs: logs,
145
+ skipped: sortSkipped(skipped),
146
+ missing: missing,
147
+ unreadable: unreadable,
148
+ usedDefaultInput: !paths.length,
149
+ searched: searched,
150
+ totals: totals,
151
+ failure: (0, exports.describeFailure)(totals, config, missing.length, unreadable.length)
152
+ }];
153
+ }
154
+ });
155
+ }); };
156
+ exports.processLogCheck = processLogCheck;
157
+ /**
158
+ * Where a run with no arguments looks. The project's results folder is where
159
+ * `run`, `job run` and `test` leave their logs, which makes the bare command
160
+ * do the useful thing inside a project.
161
+ */
162
+ var defaultInputPath = function (config) {
163
+ var projectDir = process.projectDir || process.cwd();
164
+ return path_1.default.resolve(projectDir, config.folder || logCheckConfig_1.DEFAULT_FOLDER);
165
+ };
166
+ exports.defaultInputPath = defaultInputPath;
167
+ /**
168
+ * Adds up what the run found. Skipped logs contribute nothing but are counted
169
+ * so the report can say how many files it passed over.
170
+ */
171
+ var sumTotals = function (scanned, fileCount) {
172
+ var e_2, _a;
173
+ if (fileCount === void 0) { fileCount = scanned.length; }
174
+ var totals = __assign(__assign({}, (0, types_1.emptyCounts)()), { suppressed: 0, logs: scanned.length, skipped: fileCount - scanned.length });
175
+ try {
176
+ for (var scanned_1 = __values(scanned), scanned_1_1 = scanned_1.next(); !scanned_1_1.done; scanned_1_1 = scanned_1.next()) {
177
+ var log = scanned_1_1.value;
178
+ totals.errors += log.counts.errors;
179
+ totals.warnings += log.counts.warnings;
180
+ totals.notes += log.counts.notes;
181
+ totals.suppressed += log.suppressed.length;
182
+ }
183
+ }
184
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
185
+ finally {
186
+ try {
187
+ if (scanned_1_1 && !scanned_1_1.done && (_a = scanned_1.return)) _a.call(scanned_1);
188
+ }
189
+ finally { if (e_2) throw e_2.error; }
190
+ }
191
+ return totals;
192
+ };
193
+ exports.sumTotals = sumTotals;
194
+ /**
195
+ * The verdict, as a phrase for the report.
196
+ *
197
+ * An ERROR always fails unless the policy is `never`; warnings and notes fail
198
+ * only when the policy asks for them, because notable notes are what legacy
199
+ * code emits in bulk and a gate that is red on its first run is a gate that
200
+ * gets commented out.
201
+ *
202
+ * A warnings budget is checked first and applies even under `never`: writing
203
+ * `maxWarnings: 0` is a specific instruction, and a specific instruction
204
+ * outranks a blanket one. `never` therefore means "do not fail on what you
205
+ * find", not "ignore the budget I also asked for".
206
+ *
207
+ * A path that could not be checked fails the run ahead of everything else: the
208
+ * answer to "is this log clean" is unknown, and unknown is not a pass.
209
+ */
210
+ var describeFailure = function (totals, config, missingCount, unreadableCount) {
211
+ if (missingCount === void 0) { missingCount = 0; }
212
+ if (unreadableCount === void 0) { unreadableCount = 0; }
213
+ if (missingCount) {
214
+ return "".concat(missingCount, " path").concat(missingCount === 1 ? '' : 's', " not found");
215
+ }
216
+ if (unreadableCount) {
217
+ return "".concat(unreadableCount, " folder").concat(unreadableCount === 1 ? '' : 's', " could not be read");
218
+ }
219
+ if (config.maxWarnings !== undefined &&
220
+ totals.warnings > config.maxWarnings) {
221
+ return "".concat(plural(totals.warnings, 'warning'), " (budget ").concat(config.maxWarnings, ")");
222
+ }
223
+ if (config.failOn === 'never')
224
+ return undefined;
225
+ if (totals.errors)
226
+ return plural(totals.errors, 'error');
227
+ if (config.failOn === 'warning' || config.failOn === 'note') {
228
+ if (totals.warnings)
229
+ return plural(totals.warnings, 'warning');
230
+ }
231
+ if (config.failOn === 'note' && totals.notes) {
232
+ return plural(totals.notes, 'note');
233
+ }
234
+ return undefined;
235
+ };
236
+ exports.describeFailure = describeFailure;
237
+ var plural = function (count, noun) {
238
+ return "".concat(count, " ").concat(noun).concat(count === 1 ? '' : 's');
239
+ };
240
+ var sortSkipped = function (skipped) {
241
+ return __spreadArray([], __read(skipped), false).sort(function (a, b) { return a.displayPath.localeCompare(b.displayPath); });
242
+ };
@@ -0,0 +1,128 @@
1
+ import { LogIssue } from '@sasjs/utils/logs';
2
+ import { LogCheckConfig, LogCheckSummary, OutputFormat, ScannedLog } from './types';
3
+ /** Longest message shown in the human report before it is cut short. */
4
+ export declare const MAX_MESSAGE_WIDTH = 110;
5
+ /** An issue as a report states it: line and column counted from one. */
6
+ export interface ReportIssue {
7
+ line: number;
8
+ endLine: number;
9
+ column: number;
10
+ endColumn: number;
11
+ severity: LogIssue['severity'];
12
+ code?: string;
13
+ message: string;
14
+ }
15
+ export interface RenderOptions {
16
+ format?: OutputFormat;
17
+ /** List every file that was skipped, rather than just counting them. */
18
+ verbose?: boolean;
19
+ }
20
+ /**
21
+ * Prints the result in the requested format.
22
+ *
23
+ * The three formats are three views of one summary, never three scans: the
24
+ * terminal report, the JSON document and the CI annotations must all be
25
+ * describing the same parse, or they will disagree about the same log.
26
+ */
27
+ export declare const renderLogCheck: (summary: LogCheckSummary, config: LogCheckConfig, options?: RenderOptions) => void;
28
+ /**
29
+ * The human report: what was checked, what was found, and the verdict.
30
+ *
31
+ * Colour carries severity and nothing else, so a reader scanning for trouble
32
+ * has one thing to look at, and the same report pasted into a ticket or
33
+ * piped into a log file without a terminal is still readable.
34
+ */
35
+ export declare const renderHuman: (summary: LogCheckSummary, config: LogCheckConfig, verbose?: boolean) => void;
36
+ /** The one-line-per-file table: what each log carried, and whether it failed. */
37
+ export declare const fileSummary: (scanned: ScannedLog[], config?: LogCheckConfig) => string[];
38
+ /**
39
+ * Whether a single log breaks the policy.
40
+ *
41
+ * The warnings budget is deliberately not applied here: it is a budget for the
42
+ * whole run, so a log carrying three warnings is not the one that broke it.
43
+ */
44
+ export declare const fileFails: (counts: ScannedLog["counts"], config: LogCheckConfig) => boolean;
45
+ /**
46
+ * The issues themselves, grouped under the log they came from.
47
+ *
48
+ * This is the shape a compiler prints and a reader already knows how to scan:
49
+ * the file once, then `line:column`, the severity, the SAS code where there is
50
+ * one, and the message.
51
+ */
52
+ export declare const issueDetails: (scanned: ScannedLog[]) => string[];
53
+ /**
54
+ * Lays rows out in aligned columns, with the header underlined by dashes.
55
+ *
56
+ * Colour codes take no space on screen, so widths are measured on the text
57
+ * with the codes removed - otherwise every coloured cell is padded by the
58
+ * length of its own escape sequences and the table drifts.
59
+ */
60
+ export declare const columns: (rows: string[][], head: string[], gap?: string, indent?: string) => string[];
61
+ /** `13:5` for a one-line message, `13-15:5` for one SAS wrapped. */
62
+ export declare const lineLabel: (issue: LogIssue) => string;
63
+ export declare const severityLabel: (severity: LogIssue["severity"]) => string;
64
+ /** Trims a message to the report width, on a word boundary where there is one. */
65
+ export declare const truncate: (message: string) => string;
66
+ /**
67
+ * The one-line answer: what was found, and over how many logs.
68
+ *
69
+ * It names the logs that were checked rather than the files that were not,
70
+ * because the number a reader compares against is the size of the result they
71
+ * have just looked at.
72
+ */
73
+ export declare const summarise: (summary: LogCheckSummary, scannedCount: number) => string;
74
+ /**
75
+ * The machine report.
76
+ *
77
+ * Line and column are one-based here, as an editor and a CI annotation count
78
+ * them, while the parser works in indexes - the conversion happens once, at
79
+ * this boundary, so no consumer has to remember which convention it holds.
80
+ */
81
+ export declare const toJson: (summary: LogCheckSummary, config: LogCheckConfig) => {
82
+ tool: string;
83
+ version: number;
84
+ config: {
85
+ failOn: import("./types").FailOn;
86
+ maxWarnings: number | null;
87
+ ignoreCodes: string[];
88
+ ignorePaths: string[];
89
+ };
90
+ summary: {
91
+ logs: number;
92
+ skipped: number;
93
+ errors: number;
94
+ warnings: number;
95
+ notes: number;
96
+ suppressed: number;
97
+ failed: boolean;
98
+ failure: string | null;
99
+ };
100
+ searched: string[];
101
+ missing: string[];
102
+ unreadable: {
103
+ folder: string;
104
+ reason: string;
105
+ }[];
106
+ logs: {
107
+ file: string;
108
+ errors: number;
109
+ warnings: number;
110
+ notes: number;
111
+ suppressed: number;
112
+ skipped: string | null;
113
+ issues: ReportIssue[];
114
+ }[];
115
+ skipped: {
116
+ file: string;
117
+ reason: string;
118
+ }[];
119
+ };
120
+ export declare const toReportIssue: (issue: LogIssue) => ReportIssue;
121
+ /**
122
+ * GitHub Actions annotations, one line per issue.
123
+ *
124
+ * The workflow commands escape three characters in the message and five in the
125
+ * properties; doing it here means SAS's own text arrives intact, commas and
126
+ * colons included.
127
+ */
128
+ export declare const toAnnotations: (summary: LogCheckSummary) => string[];