@factorialco/fcode-cli 1.1.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.
- package/README.md +1021 -0
- package/bin/dev +17 -0
- package/bin/dev.cmd +3 -0
- package/bin/run +7 -0
- package/bin/run.cmd +3 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.js +22 -0
- package/dist/commands/clone.d.ts +26 -0
- package/dist/commands/clone.js +177 -0
- package/dist/commands/dependencies/add.d.ts +9 -0
- package/dist/commands/dependencies/add.js +13 -0
- package/dist/commands/dependencies/install.d.ts +14 -0
- package/dist/commands/dependencies/install.js +44 -0
- package/dist/commands/dependencies/pull.d.ts +12 -0
- package/dist/commands/dependencies/pull.js +14 -0
- package/dist/commands/dependencies/push.d.ts +12 -0
- package/dist/commands/dependencies/push.js +14 -0
- package/dist/commands/dependencies/reset.d.ts +9 -0
- package/dist/commands/dependencies/reset.js +13 -0
- package/dist/commands/dependencies/status.d.ts +9 -0
- package/dist/commands/dependencies/status.js +13 -0
- package/dist/commands/http.d.ts +22 -0
- package/dist/commands/http.js +272 -0
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.js +62 -0
- package/dist/commands/logout.d.ts +10 -0
- package/dist/commands/logout.js +34 -0
- package/dist/commands/modules/add.d.ts +9 -0
- package/dist/commands/modules/add.js +13 -0
- package/dist/commands/modules/pull.d.ts +13 -0
- package/dist/commands/modules/pull.js +43 -0
- package/dist/commands/modules/push.d.ts +13 -0
- package/dist/commands/modules/push.js +35 -0
- package/dist/commands/modules/remove.d.ts +9 -0
- package/dist/commands/modules/remove.js +19 -0
- package/dist/commands/modules/reset.d.ts +9 -0
- package/dist/commands/modules/reset.js +13 -0
- package/dist/commands/modules/status.d.ts +11 -0
- package/dist/commands/modules/status.js +40 -0
- package/dist/commands/pre-debug.d.ts +20 -0
- package/dist/commands/pre-debug.js +120 -0
- package/dist/commands/process-metadata.d.ts +18 -0
- package/dist/commands/process-metadata.js +63 -0
- package/dist/commands/processes/add.d.ts +9 -0
- package/dist/commands/processes/add.js +13 -0
- package/dist/commands/processes/install-dependencies.d.ts +14 -0
- package/dist/commands/processes/install-dependencies.js +30 -0
- package/dist/commands/processes/pull.d.ts +13 -0
- package/dist/commands/processes/pull.js +47 -0
- package/dist/commands/processes/push.d.ts +13 -0
- package/dist/commands/processes/push.js +39 -0
- package/dist/commands/processes/remove.d.ts +9 -0
- package/dist/commands/processes/remove.js +19 -0
- package/dist/commands/processes/reset.d.ts +9 -0
- package/dist/commands/processes/reset.js +13 -0
- package/dist/commands/processes/status.d.ts +11 -0
- package/dist/commands/processes/status.js +58 -0
- package/dist/commands/pull.d.ts +9 -0
- package/dist/commands/pull.js +32 -0
- package/dist/commands/push.d.ts +9 -0
- package/dist/commands/push.js +29 -0
- package/dist/commands/remote/add.d.ts +10 -0
- package/dist/commands/remote/add.js +32 -0
- package/dist/commands/remote/list.d.ts +7 -0
- package/dist/commands/remote/list.js +33 -0
- package/dist/commands/remote/set.d.ts +10 -0
- package/dist/commands/remote/set.js +28 -0
- package/dist/commands/reset.d.ts +6 -0
- package/dist/commands/reset.js +19 -0
- package/dist/commands/run.d.ts +16 -0
- package/dist/commands/run.js +56 -0
- package/dist/commands/runtime-env.d.ts +12 -0
- package/dist/commands/runtime-env.js +42 -0
- package/dist/commands/settings/get.d.ts +12 -0
- package/dist/commands/settings/get.js +67 -0
- package/dist/commands/settings/remove.d.ts +9 -0
- package/dist/commands/settings/remove.js +35 -0
- package/dist/commands/settings/set.d.ts +10 -0
- package/dist/commands/settings/set.js +39 -0
- package/dist/commands/setup-debug.d.ts +12 -0
- package/dist/commands/setup-debug.js +118 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +22 -0
- package/dist/commands/team/pull.d.ts +8 -0
- package/dist/commands/team/pull.js +23 -0
- package/dist/commands/team/push.d.ts +11 -0
- package/dist/commands/team/push.js +41 -0
- package/dist/commands/team/status.d.ts +8 -0
- package/dist/commands/team/status.js +40 -0
- package/dist/commands/test/scaffold.d.ts +9 -0
- package/dist/commands/test/scaffold.js +61 -0
- package/dist/commands/test.d.ts +13 -0
- package/dist/commands/test.js +72 -0
- package/dist/commands/variables/add.d.ts +12 -0
- package/dist/commands/variables/add.js +21 -0
- package/dist/commands/variables/pull.d.ts +12 -0
- package/dist/commands/variables/pull.js +14 -0
- package/dist/commands/variables/push.d.ts +12 -0
- package/dist/commands/variables/push.js +14 -0
- package/dist/commands/variables/remove.d.ts +9 -0
- package/dist/commands/variables/remove.js +19 -0
- package/dist/commands/variables/reset.d.ts +9 -0
- package/dist/commands/variables/reset.js +13 -0
- package/dist/commands/variables/status.d.ts +12 -0
- package/dist/commands/variables/status.js +58 -0
- package/dist/config/global.d.ts +15 -0
- package/dist/config/global.js +77 -0
- package/dist/config/index.d.ts +14 -0
- package/dist/config/index.js +44 -0
- package/dist/config/remote.d.ts +15 -0
- package/dist/config/remote.js +60 -0
- package/dist/config/settings.d.ts +16 -0
- package/dist/config/settings.js +54 -0
- package/dist/config/workspace.d.ts +28 -0
- package/dist/config/workspace.js +158 -0
- package/dist/core/commands/api-client-command.d.ts +6 -0
- package/dist/core/commands/api-client-command.js +30 -0
- package/dist/core/commands/base-command.d.ts +11 -0
- package/dist/core/commands/base-command.js +61 -0
- package/dist/core/commands/dependency-command.d.ts +5 -0
- package/dist/core/commands/dependency-command.js +16 -0
- package/dist/core/commands/modules-command.d.ts +7 -0
- package/dist/core/commands/modules-command.js +18 -0
- package/dist/core/commands/processes-command.d.ts +8 -0
- package/dist/core/commands/processes-command.js +27 -0
- package/dist/core/commands/resource-command.d.ts +24 -0
- package/dist/core/commands/resource-command.js +92 -0
- package/dist/core/commands/variables-command.d.ts +5 -0
- package/dist/core/commands/variables-command.js +16 -0
- package/dist/core/dependencies/env-dependencies-command.d.ts +19 -0
- package/dist/core/dependencies/env-dependencies-command.js +70 -0
- package/dist/core/dependencies/env-process-dependencies-command.d.ts +15 -0
- package/dist/core/dependencies/env-process-dependencies-command.js +78 -0
- package/dist/core/dependencies/javascript-env-dependencies.d.ts +8 -0
- package/dist/core/dependencies/javascript-env-dependencies.js +77 -0
- package/dist/core/dependencies/python-env-dependencies.d.ts +9 -0
- package/dist/core/dependencies/python-env-dependencies.js +91 -0
- package/dist/core/errors.d.ts +55 -0
- package/dist/core/errors.js +144 -0
- package/dist/core/executors/execution-task.d.ts +29 -0
- package/dist/core/executors/execution-task.js +139 -0
- package/dist/core/executors/javascript-execution-task.d.ts +13 -0
- package/dist/core/executors/javascript-execution-task.js +618 -0
- package/dist/core/executors/python/context.d.ts +2 -0
- package/dist/core/executors/python/context.js +3 -0
- package/dist/core/executors/python/datastore.d.ts +2 -0
- package/dist/core/executors/python/datastore.js +4 -0
- package/dist/core/executors/python-execution-task.d.ts +11 -0
- package/dist/core/executors/python-execution-task.js +173 -0
- package/dist/core/executors/source-code-sanitizer.d.ts +4 -0
- package/dist/core/executors/source-code-sanitizer.js +45 -0
- package/dist/core/resource-merger.d.ts +14 -0
- package/dist/core/resource-merger.js +155 -0
- package/dist/core/resource-pusher.d.ts +12 -0
- package/dist/core/resource-pusher.js +133 -0
- package/dist/core/resource-repository.d.ts +8 -0
- package/dist/core/resource-repository.js +2 -0
- package/dist/core/types.d.ts +290 -0
- package/dist/core/types.js +238 -0
- package/dist/core/utils/dependencies.d.ts +5 -0
- package/dist/core/utils/dependencies.js +82 -0
- package/dist/core/utils/variables.d.ts +4 -0
- package/dist/core/utils/variables.js +10 -0
- package/dist/hooks/init/auth.d.ts +3 -0
- package/dist/hooks/init/auth.js +28 -0
- package/dist/hooks/init/update.d.ts +3 -0
- package/dist/hooks/init/update.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/repositories/process.d.ts +36 -0
- package/dist/repositories/process.js +339 -0
- package/dist/repositories/programming-language-manifest.d.ts +17 -0
- package/dist/repositories/programming-language-manifest.js +95 -0
- package/dist/repositories/resource.d.ts +29 -0
- package/dist/repositories/resource.js +79 -0
- package/dist/repositories/script-library.d.ts +28 -0
- package/dist/repositories/script-library.js +164 -0
- package/dist/repositories/team.d.ts +23 -0
- package/dist/repositories/team.js +74 -0
- package/dist/repositories/variable.d.ts +26 -0
- package/dist/repositories/variable.js +174 -0
- package/dist/runtime/executor.js +87 -0
- package/dist/runtime/executor.py +81 -0
- package/dist/runtime/libs/node/fcode/index.js +1 -0
- package/dist/runtime/libs/python/fcode/__init__.py +75 -0
- package/dist/runtime/libs/python/fcode/context.py +98 -0
- package/dist/runtime/libs/python/fcode/datastore.py +35 -0
- package/dist/runtime/libs/python/fcode/schedule.py +45 -0
- package/dist/runtime/libs/python/fcode/storage.py +271 -0
- package/dist/runtime/libs/python/fcode/variables.py +102 -0
- package/dist/services/auth-service.d.ts +12 -0
- package/dist/services/auth-service.js +114 -0
- package/dist/services/credential-prompter.d.ts +6 -0
- package/dist/services/credential-prompter.js +60 -0
- package/dist/services/fcode-graphql-client.d.ts +45 -0
- package/dist/services/fcode-graphql-client.js +721 -0
- package/dist/services/http-server.d.ts +3 -0
- package/dist/services/http-server.js +17 -0
- package/dist/services/oauth-browser-service.d.ts +9 -0
- package/dist/services/oauth-browser-service.js +164 -0
- package/dist/services/process-parameters-sampler.d.ts +2 -0
- package/dist/services/process-parameters-sampler.js +41 -0
- package/dist/services/process-runner.d.ts +22 -0
- package/dist/services/process-runner.js +97 -0
- package/dist/services/resource-checker.d.ts +8 -0
- package/dist/services/resource-checker.js +82 -0
- package/dist/services/test-runner.d.ts +17 -0
- package/dist/services/test-runner.js +228 -0
- package/dist/utils/canonical-hash.d.ts +9 -0
- package/dist/utils/canonical-hash.js +30 -0
- package/dist/utils/cli/index.d.ts +14 -0
- package/dist/utils/cli/index.js +50 -0
- package/dist/utils/cli/screen.d.ts +2 -0
- package/dist/utils/cli/screen.js +25 -0
- package/dist/utils/cli/styled/table.d.ts +23 -0
- package/dist/utils/cli/styled/table.js +272 -0
- package/dist/utils/colorize-resource.d.ts +1 -0
- package/dist/utils/colorize-resource.js +7 -0
- package/dist/utils/env-utils.d.ts +2 -0
- package/dist/utils/env-utils.js +16 -0
- package/dist/utils/file-utils.d.ts +11 -0
- package/dist/utils/file-utils.js +33 -0
- package/dist/utils/filenamify.d.ts +4 -0
- package/dist/utils/filenamify.js +73 -0
- package/dist/utils/gitignore.d.ts +17 -0
- package/dist/utils/gitignore.js +60 -0
- package/dist/utils/promise.d.ts +4 -0
- package/dist/utils/promise.js +38 -0
- package/dist/utils/url-utils.d.ts +2 -0
- package/dist/utils/url-utils.js +10 -0
- package/dist/utils/variable-warnings.d.ts +8 -0
- package/dist/utils/variable-warnings.js +38 -0
- package/oclif.manifest.json +1783 -0
- package/package.json +129 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runProcessTests = runProcessTests;
|
|
4
|
+
exports.printTestResults = printTestResults;
|
|
5
|
+
const fs = require("fs-extra");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const _ = require("lodash");
|
|
8
|
+
const color_1 = require("@oclif/color");
|
|
9
|
+
const cli_1 = require("../utils/cli");
|
|
10
|
+
const process_runner_1 = require("./process-runner");
|
|
11
|
+
const errors_1 = require("../core/errors");
|
|
12
|
+
const variable_1 = require("../repositories/variable");
|
|
13
|
+
function hooksFileName(programmingLanguage) {
|
|
14
|
+
return programmingLanguage === 'PYTHON' ? 'testHooks.py' : 'testHooks.js';
|
|
15
|
+
}
|
|
16
|
+
function discoverTests(processSlug, programmingLanguage) {
|
|
17
|
+
const testsDir = path.join(process.cwd(), 'processes', processSlug, 'tests');
|
|
18
|
+
if (!fs.existsSync(testsDir))
|
|
19
|
+
return null;
|
|
20
|
+
const hooksFile = hooksFileName(programmingLanguage);
|
|
21
|
+
const entries = fs.readdirSync(testsDir, { withFileTypes: true });
|
|
22
|
+
const testDirs = entries
|
|
23
|
+
.filter((e) => e.isDirectory())
|
|
24
|
+
.map((e) => e.name)
|
|
25
|
+
.sort();
|
|
26
|
+
return testDirs
|
|
27
|
+
.map((name) => {
|
|
28
|
+
const dir = path.join(testsDir, name);
|
|
29
|
+
const inputPath = path.join(dir, 'input.json');
|
|
30
|
+
if (!fs.existsSync(inputPath))
|
|
31
|
+
return null;
|
|
32
|
+
const outputCandidate = path.join(dir, 'output.json');
|
|
33
|
+
const errorCandidate = path.join(dir, 'error.json');
|
|
34
|
+
const variablesCandidate = path.join(dir, 'variables.test.env');
|
|
35
|
+
const hooksCandidate = path.join(dir, hooksFile);
|
|
36
|
+
return {
|
|
37
|
+
name,
|
|
38
|
+
inputPath,
|
|
39
|
+
outputPath: fs.existsSync(outputCandidate) ? outputCandidate : null,
|
|
40
|
+
errorPath: fs.existsSync(errorCandidate) ? errorCandidate : null,
|
|
41
|
+
variablesPath: fs.existsSync(variablesCandidate) ? variablesCandidate : null,
|
|
42
|
+
hooksFilePath: fs.existsSync(hooksCandidate) ? hooksCandidate : null,
|
|
43
|
+
};
|
|
44
|
+
})
|
|
45
|
+
.filter((tc) => tc !== null);
|
|
46
|
+
}
|
|
47
|
+
function getGlobalTestAssets(processSlug, programmingLanguage) {
|
|
48
|
+
const testsDir = path.join(process.cwd(), 'processes', processSlug, 'tests');
|
|
49
|
+
const hooksCandidate = path.join(testsDir, hooksFileName(programmingLanguage));
|
|
50
|
+
const variablesCandidate = path.join(testsDir, 'variables.test.env');
|
|
51
|
+
return {
|
|
52
|
+
hooksFilePath: fs.existsSync(hooksCandidate) ? hooksCandidate : null,
|
|
53
|
+
variablesPath: fs.existsSync(variablesCandidate) ? variablesCandidate : null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function runProcessTests(fcodeProcess, options = {}) {
|
|
57
|
+
const { logLevel = 'ERROR', jsonLogs = false } = options;
|
|
58
|
+
const { slug, script: { programmingLanguage } } = fcodeProcess;
|
|
59
|
+
const testCases = discoverTests(slug, programmingLanguage);
|
|
60
|
+
if (!testCases) {
|
|
61
|
+
return { processSlug: slug, results: [] };
|
|
62
|
+
}
|
|
63
|
+
const globalAssets = getGlobalTestAssets(slug, programmingLanguage);
|
|
64
|
+
const hookOptions = { logLevel, jsonLogs };
|
|
65
|
+
const variableRepository = new variable_1.default();
|
|
66
|
+
let baseVariables = await variableRepository.findAllForExecution();
|
|
67
|
+
if (globalAssets.variablesPath) {
|
|
68
|
+
baseVariables = (0, process_runner_1.mergeVariables)(baseVariables, (0, process_runner_1.loadVariablesFromFile)(globalAssets.variablesPath));
|
|
69
|
+
}
|
|
70
|
+
const results = [];
|
|
71
|
+
// Run global before hook
|
|
72
|
+
let globalBeforeFailed = false;
|
|
73
|
+
if (globalAssets.hooksFilePath) {
|
|
74
|
+
try {
|
|
75
|
+
await (0, process_runner_1.runHook)(globalAssets.hooksFilePath, fcodeProcess, baseVariables, { ...hookOptions, hookFunction: 'before' });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
globalBeforeFailed = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const testCase of testCases) {
|
|
82
|
+
const start = Date.now();
|
|
83
|
+
if (globalBeforeFailed) {
|
|
84
|
+
results.push({ name: testCase.name, passed: false, error: 'Global before hook failed', durationMs: 0 });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
// Build per-test variables
|
|
88
|
+
const testVariables = testCase.variablesPath
|
|
89
|
+
? (0, process_runner_1.mergeVariables)(baseVariables, (0, process_runner_1.loadVariablesFromFile)(testCase.variablesPath))
|
|
90
|
+
: baseVariables;
|
|
91
|
+
// Misconfiguration guard
|
|
92
|
+
if (testCase.outputPath && testCase.errorPath) {
|
|
93
|
+
results.push({ name: testCase.name, passed: false, error: 'Test misconfiguration: output.json and error.json cannot coexist', durationMs: Date.now() - start });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Parse input
|
|
97
|
+
let parameters;
|
|
98
|
+
try {
|
|
99
|
+
parameters = JSON.parse(fs.readFileSync(testCase.inputPath, 'utf8'));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
results.push({ name: testCase.name, passed: false, error: 'Failed to parse input.json', durationMs: Date.now() - start });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Per-test before hook
|
|
106
|
+
if (testCase.hooksFilePath) {
|
|
107
|
+
try {
|
|
108
|
+
await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'before' });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
results.push({ name: testCase.name, passed: false, error: 'Before hook failed', durationMs: Date.now() - start });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let passed = true;
|
|
116
|
+
let errorMsg;
|
|
117
|
+
if (testCase.errorPath) {
|
|
118
|
+
// Expected-error test: process must fail
|
|
119
|
+
let expectedMessage;
|
|
120
|
+
try {
|
|
121
|
+
const errorSpec = JSON.parse(fs.readFileSync(testCase.errorPath, 'utf8'));
|
|
122
|
+
expectedMessage = errorSpec?.message;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
results.push({ name: testCase.name, passed: false, error: 'Failed to parse error.json', durationMs: Date.now() - start });
|
|
126
|
+
if (testCase.hooksFilePath) {
|
|
127
|
+
await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
await (0, process_runner_1.runProcess)({ slug, parameters, variablesOverride: testVariables, logLevel, jsonLogs, suppressElapsedLog: true });
|
|
133
|
+
passed = false;
|
|
134
|
+
errorMsg = 'Expected process to fail but it succeeded';
|
|
135
|
+
}
|
|
136
|
+
catch (runError) {
|
|
137
|
+
const actualMessage = runError instanceof errors_1.ErrorExecutingProcess ? runError.message : String(runError);
|
|
138
|
+
if (expectedMessage && !actualMessage.includes(expectedMessage)) {
|
|
139
|
+
passed = false;
|
|
140
|
+
errorMsg = `Error message mismatch\n Expected to contain: ${expectedMessage}\n Received: ${actualMessage}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Normal test: process must succeed
|
|
146
|
+
let actualOutput;
|
|
147
|
+
try {
|
|
148
|
+
actualOutput = await (0, process_runner_1.runProcess)({ slug, parameters, variablesOverride: testVariables, logLevel, jsonLogs, suppressElapsedLog: true });
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
passed = false;
|
|
152
|
+
errorMsg = `Process execution failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
153
|
+
if (testCase.hooksFilePath) {
|
|
154
|
+
await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
|
|
155
|
+
}
|
|
156
|
+
results.push({ name: testCase.name, passed, error: errorMsg, durationMs: Date.now() - start });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (testCase.outputPath) {
|
|
160
|
+
try {
|
|
161
|
+
const expected = JSON.parse(fs.readFileSync(testCase.outputPath, 'utf8'));
|
|
162
|
+
if (!_.isEqual(actualOutput, expected)) {
|
|
163
|
+
passed = false;
|
|
164
|
+
errorMsg = `Expected: ${JSON.stringify(expected)}\n Received: ${JSON.stringify(actualOutput)}`;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
passed = false;
|
|
169
|
+
errorMsg = 'Failed to parse output.json';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
results.push({ name: testCase.name, passed, error: errorMsg, durationMs: Date.now() - start });
|
|
174
|
+
// Per-test after hook
|
|
175
|
+
if (testCase.hooksFilePath) {
|
|
176
|
+
await (0, process_runner_1.runHook)(testCase.hooksFilePath, fcodeProcess, testVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Global after hook
|
|
180
|
+
if (globalAssets.hooksFilePath) {
|
|
181
|
+
await (0, process_runner_1.runHook)(globalAssets.hooksFilePath, fcodeProcess, baseVariables, { ...hookOptions, hookFunction: 'after' }).catch(() => { });
|
|
182
|
+
}
|
|
183
|
+
return { processSlug: slug, results };
|
|
184
|
+
}
|
|
185
|
+
function printTestResults(suiteResults) {
|
|
186
|
+
const nonEmpty = suiteResults.filter((s) => s.results.length > 0);
|
|
187
|
+
for (const suite of nonEmpty) {
|
|
188
|
+
const total = suite.results.length;
|
|
189
|
+
const passed = suite.results.filter((r) => r.passed).length;
|
|
190
|
+
cli_1.cli.log(`\nTesting ${color_1.default.bold(suite.processSlug)} (${total} test${total === 1 ? '' : 's'})`);
|
|
191
|
+
for (const result of suite.results) {
|
|
192
|
+
if (result.passed) {
|
|
193
|
+
cli_1.cli.log(` ${color_1.default.green('✓')} ${result.name} (${result.durationMs}ms)`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
cli_1.cli.log(` ${color_1.default.red('✗')} ${result.name}`);
|
|
197
|
+
if (result.error) {
|
|
198
|
+
for (const line of result.error.split('\n')) {
|
|
199
|
+
cli_1.cli.log(` ${line}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const failed = total - passed;
|
|
205
|
+
if (failed === 0) {
|
|
206
|
+
cli_1.cli.log(`\n ${color_1.default.green(`${passed} passed`)}`);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
cli_1.cli.log(`\n ${color_1.default.green(`${passed} passed`)}, ${color_1.default.red(`${failed} failed`)}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (suiteResults.length === 0)
|
|
213
|
+
return;
|
|
214
|
+
const totalTests = suiteResults.reduce((sum, s) => sum + s.results.length, 0);
|
|
215
|
+
if (totalTests === 0)
|
|
216
|
+
return;
|
|
217
|
+
const totalPassed = suiteResults.reduce((sum, s) => sum + s.results.filter((r) => r.passed).length, 0);
|
|
218
|
+
const totalFailed = totalTests - totalPassed;
|
|
219
|
+
if (nonEmpty.length > 1) {
|
|
220
|
+
cli_1.cli.log(`\n${'─'.repeat(48)}`);
|
|
221
|
+
if (totalFailed === 0) {
|
|
222
|
+
cli_1.cli.log(`Total: ${totalTests} test${totalTests === 1 ? '' : 's'} — ${color_1.default.green(`${totalPassed} passed`)}`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
cli_1.cli.log(`Total: ${totalTests} test${totalTests === 1 ? '' : 's'} — ${color_1.default.green(`${totalPassed} passed`)}, ${color_1.default.red(`${totalFailed} failed`)}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serializes a value with object keys sorted and no insignificant whitespace, so
|
|
3
|
+
* that two values that differ only in formatting (indentation, key order, trailing
|
|
4
|
+
* newline) produce an identical string. Used to hash metadata files independently
|
|
5
|
+
* of how they happen to be formatted on disk.
|
|
6
|
+
*/
|
|
7
|
+
export declare function canonicalStringify(value: unknown): string;
|
|
8
|
+
/** MD5 of the canonical serialization of `value` (see {@link canonicalStringify}). */
|
|
9
|
+
export declare function canonicalJsonHash(value: unknown): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canonicalStringify = canonicalStringify;
|
|
4
|
+
exports.canonicalJsonHash = canonicalJsonHash;
|
|
5
|
+
const crypto = require("node:crypto");
|
|
6
|
+
/**
|
|
7
|
+
* Serializes a value with object keys sorted and no insignificant whitespace, so
|
|
8
|
+
* that two values that differ only in formatting (indentation, key order, trailing
|
|
9
|
+
* newline) produce an identical string. Used to hash metadata files independently
|
|
10
|
+
* of how they happen to be formatted on disk.
|
|
11
|
+
*/
|
|
12
|
+
function canonicalStringify(value) {
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
return 'null';
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return `[${value.map((item) => canonicalStringify(item)).join(',')}]`;
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === 'object') {
|
|
20
|
+
const entries = Object.keys(value)
|
|
21
|
+
.sort()
|
|
22
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalStringify(value[key])}`);
|
|
23
|
+
return `{${entries.join(',')}}`;
|
|
24
|
+
}
|
|
25
|
+
return JSON.stringify(value);
|
|
26
|
+
}
|
|
27
|
+
/** MD5 of the canonical serialization of `value` (see {@link canonicalStringify}). */
|
|
28
|
+
function canonicalJsonHash(value) {
|
|
29
|
+
return crypto.createHash('md5').update(canonicalStringify(value)).digest('hex');
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as Table from './styled/table';
|
|
2
|
+
import { CLIError } from '../../core/errors';
|
|
3
|
+
export declare const cli: {
|
|
4
|
+
action: import("@oclif/core/lib/ux/action/simple").default | import("@oclif/core/lib/ux/action/spinner").default;
|
|
5
|
+
confirm(message: string): Promise<boolean>;
|
|
6
|
+
readonly table: typeof import("./styled/table").table;
|
|
7
|
+
error(format: string | CLIError, ...args: string[]): void;
|
|
8
|
+
info(format: string, ...args: string[]): void;
|
|
9
|
+
log(format?: string, ...args: string[]): void;
|
|
10
|
+
password(message: string): Promise<string>;
|
|
11
|
+
prompt(message: string): Promise<string>;
|
|
12
|
+
};
|
|
13
|
+
export default cli;
|
|
14
|
+
export { Table };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Table = exports.cli = void 0;
|
|
4
|
+
const Table = require("./styled/table");
|
|
5
|
+
exports.Table = Table;
|
|
6
|
+
const util = require("util");
|
|
7
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
8
|
+
const core_1 = require("@oclif/core");
|
|
9
|
+
const color_1 = require("@oclif/color");
|
|
10
|
+
const errors_1 = require("../../core/errors");
|
|
11
|
+
exports.cli = {
|
|
12
|
+
action: core_1.ux.action,
|
|
13
|
+
confirm(message) {
|
|
14
|
+
return (0, prompts_1.confirm)({ message });
|
|
15
|
+
},
|
|
16
|
+
get table() {
|
|
17
|
+
if (process.env.SILENT_MODE === 'true') {
|
|
18
|
+
return () => { };
|
|
19
|
+
}
|
|
20
|
+
return require('./styled/table').table;
|
|
21
|
+
},
|
|
22
|
+
error(format, ...args) {
|
|
23
|
+
if (format instanceof errors_1.CLIError) {
|
|
24
|
+
const message = format.message +
|
|
25
|
+
(format.suggestions && format.suggestions.length > 0
|
|
26
|
+
? ` Try this: ${format.suggestions.map((suggestion) => `'${suggestion}'`).join('\n')} \n`
|
|
27
|
+
: '');
|
|
28
|
+
this.info(color_1.default.red(`✖ `) + message);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
this.info(color_1.default.red(`✖ `) + format, ...args);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
info(format, ...args) {
|
|
35
|
+
if (process.env.SILENT_MODE === 'true') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
process.stdout.write(util.format(format, ...args) + '\n');
|
|
39
|
+
},
|
|
40
|
+
log(format, ...args) {
|
|
41
|
+
this.info(format || '', ...args);
|
|
42
|
+
},
|
|
43
|
+
password(message) {
|
|
44
|
+
return (0, prompts_1.password)({ message });
|
|
45
|
+
},
|
|
46
|
+
prompt(message) {
|
|
47
|
+
return (0, prompts_1.input)({ message });
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
exports.default = exports.cli;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.errtermwidth = exports.stdtermwidth = void 0;
|
|
4
|
+
function termwidth(stream) {
|
|
5
|
+
if (!stream.isTTY) {
|
|
6
|
+
return 80;
|
|
7
|
+
}
|
|
8
|
+
const width = stream.getWindowSize()[0];
|
|
9
|
+
if (width < 1) {
|
|
10
|
+
return 80;
|
|
11
|
+
}
|
|
12
|
+
if (width < 40) {
|
|
13
|
+
return 40;
|
|
14
|
+
}
|
|
15
|
+
return width;
|
|
16
|
+
}
|
|
17
|
+
const columns = global.columns;
|
|
18
|
+
exports.stdtermwidth = columns || termwidth(process.stdout);
|
|
19
|
+
exports.errtermwidth = columns || termwidth(process.stderr);
|
|
20
|
+
process.stdout.on('resize', () => {
|
|
21
|
+
exports.stdtermwidth = columns || termwidth(process.stdout);
|
|
22
|
+
});
|
|
23
|
+
process.stderr.on('resize', () => {
|
|
24
|
+
exports.errtermwidth = columns || termwidth(process.stderr);
|
|
25
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare function table<T extends Record<string, unknown>>(data: T[], columns: table.Columns<T>, options?: table.Options): void;
|
|
2
|
+
export declare namespace table {
|
|
3
|
+
interface Column<T extends Record<string, unknown>> {
|
|
4
|
+
header: string;
|
|
5
|
+
extended: boolean;
|
|
6
|
+
minWidth: number;
|
|
7
|
+
get(row: T): any;
|
|
8
|
+
}
|
|
9
|
+
type Columns<T extends Record<string, unknown>> = {
|
|
10
|
+
[key: string]: Partial<Column<T>>;
|
|
11
|
+
};
|
|
12
|
+
interface Options {
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
sort?: string;
|
|
15
|
+
filter?: string;
|
|
16
|
+
columns?: string;
|
|
17
|
+
extended?: boolean;
|
|
18
|
+
'no-truncate'?: boolean;
|
|
19
|
+
output?: string;
|
|
20
|
+
'no-header'?: boolean;
|
|
21
|
+
printLine?(s: any): any;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.table = table;
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
const _ = require("lodash");
|
|
6
|
+
const util_1 = require("util");
|
|
7
|
+
const screen_1 = require("../screen");
|
|
8
|
+
const sw = require('string-width');
|
|
9
|
+
const { orderBy } = require('natural-orderby');
|
|
10
|
+
class Table {
|
|
11
|
+
constructor(data, columns, options = {}) {
|
|
12
|
+
this.data = data;
|
|
13
|
+
// assign columns
|
|
14
|
+
this.columns = Object.keys(columns).map((key) => {
|
|
15
|
+
const col = columns[key];
|
|
16
|
+
const extended = col.extended || false;
|
|
17
|
+
const get = col.get || ((row) => row[key]);
|
|
18
|
+
const header = typeof col.header === 'string' ? col.header : _.capitalize(key.replace(/_/g, ' '));
|
|
19
|
+
const minWidth = Math.max(col.minWidth || 0, sw(header) + 1);
|
|
20
|
+
return {
|
|
21
|
+
extended,
|
|
22
|
+
get,
|
|
23
|
+
header,
|
|
24
|
+
key,
|
|
25
|
+
minWidth,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
// assign options
|
|
29
|
+
const { columns: cols, filter, csv, output, extended, sort, title, printLine } = options;
|
|
30
|
+
this.options = {
|
|
31
|
+
columns: cols,
|
|
32
|
+
output: csv ? 'csv' : output,
|
|
33
|
+
extended,
|
|
34
|
+
filter,
|
|
35
|
+
'no-header': options['no-header'] || false,
|
|
36
|
+
'no-truncate': options['no-truncate'] || false,
|
|
37
|
+
printLine: printLine || ((s) => process.stdout.write(s + '\n')),
|
|
38
|
+
rowStart: ' ',
|
|
39
|
+
sort,
|
|
40
|
+
title,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
display() {
|
|
44
|
+
// build table rows from input array data
|
|
45
|
+
let rows = this.data.map((d) => {
|
|
46
|
+
const row = {};
|
|
47
|
+
for (const col of this.columns) {
|
|
48
|
+
let val = col.get(d);
|
|
49
|
+
if (typeof val !== 'string')
|
|
50
|
+
val = (0, util_1.inspect)(val, { breakLength: Number.POSITIVE_INFINITY });
|
|
51
|
+
row[col.key] = val;
|
|
52
|
+
}
|
|
53
|
+
return row;
|
|
54
|
+
});
|
|
55
|
+
// filter rows
|
|
56
|
+
if (this.options.filter) {
|
|
57
|
+
/* eslint-disable-next-line prefer-const */
|
|
58
|
+
let [header, regex] = this.options.filter.split('=');
|
|
59
|
+
const isNot = header[0] === '-';
|
|
60
|
+
if (isNot)
|
|
61
|
+
header = header.slice(1);
|
|
62
|
+
const col = this.findColumnFromHeader(header);
|
|
63
|
+
if (!col || !regex)
|
|
64
|
+
throw new Error('Filter flag has an invalid value');
|
|
65
|
+
rows = rows.filter((d) => {
|
|
66
|
+
const re = new RegExp(regex);
|
|
67
|
+
const val = d[col.key];
|
|
68
|
+
const match = val.match(re);
|
|
69
|
+
return isNot ? !match : match;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// sort rows
|
|
73
|
+
if (this.options.sort) {
|
|
74
|
+
const sorters = this.options.sort.split(',');
|
|
75
|
+
const sortHeaders = sorters.map((k) => (k[0] === '-' ? k.slice(1) : k));
|
|
76
|
+
const sortKeys = this.filterColumnsFromHeaders(sortHeaders).map((c) => {
|
|
77
|
+
return (v) => v[c.key];
|
|
78
|
+
});
|
|
79
|
+
const sortKeysOrder = sorters.map((k) => (k[0] === '-' ? 'desc' : 'asc'));
|
|
80
|
+
rows = orderBy(rows, sortKeys, sortKeysOrder);
|
|
81
|
+
}
|
|
82
|
+
// and filter columns
|
|
83
|
+
if (this.options.columns) {
|
|
84
|
+
const filters = this.options.columns.split(',');
|
|
85
|
+
this.columns = this.filterColumnsFromHeaders(filters);
|
|
86
|
+
}
|
|
87
|
+
else if (!this.options.extended) {
|
|
88
|
+
// show extented columns/properties
|
|
89
|
+
this.columns = this.columns.filter((c) => !c.extended);
|
|
90
|
+
}
|
|
91
|
+
this.data = rows;
|
|
92
|
+
switch (this.options.output) {
|
|
93
|
+
case 'csv':
|
|
94
|
+
this.outputCSV();
|
|
95
|
+
break;
|
|
96
|
+
case 'json':
|
|
97
|
+
this.outputJSON();
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
this.outputTable();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
findColumnFromHeader(header) {
|
|
104
|
+
return this.columns.find((c) => c.header.toLowerCase() === header.toLowerCase());
|
|
105
|
+
}
|
|
106
|
+
filterColumnsFromHeaders(filters) {
|
|
107
|
+
// unique
|
|
108
|
+
filters = [...new Set(filters)];
|
|
109
|
+
const cols = [];
|
|
110
|
+
for (const f of filters) {
|
|
111
|
+
const c = this.columns.find((c) => c.header.toLowerCase() === f.toLowerCase());
|
|
112
|
+
if (c)
|
|
113
|
+
cols.push(c);
|
|
114
|
+
}
|
|
115
|
+
return cols;
|
|
116
|
+
}
|
|
117
|
+
getCSVRow(d) {
|
|
118
|
+
const values = this.columns.map((col) => d[col.key] || '');
|
|
119
|
+
const lineToBeEscaped = values.find((e) => e.includes('"') || e.includes('\n') || e.includes('\r\n') || e.includes('\r') || e.includes(','));
|
|
120
|
+
return values.map((e) => (lineToBeEscaped ? `"${e.replace('"', '""')}"` : e));
|
|
121
|
+
}
|
|
122
|
+
resolveColumnsToObjectArray() {
|
|
123
|
+
// tslint:disable-next-line:no-this-assignment
|
|
124
|
+
const { data, columns } = this;
|
|
125
|
+
return data.map((d) => {
|
|
126
|
+
// eslint-disable-next-line unicorn/prefer-object-from-entries
|
|
127
|
+
return columns.reduce((obj, col) => {
|
|
128
|
+
return {
|
|
129
|
+
...obj,
|
|
130
|
+
[col.key]: d[col.key] || '',
|
|
131
|
+
};
|
|
132
|
+
}, {});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
outputJSON() {
|
|
136
|
+
this.options.printLine(JSON.stringify(this.resolveColumnsToObjectArray(), undefined, 2));
|
|
137
|
+
}
|
|
138
|
+
outputCSV() {
|
|
139
|
+
// tslint:disable-next-line:no-this-assignment
|
|
140
|
+
const { data, columns, options } = this;
|
|
141
|
+
if (!options['no-header']) {
|
|
142
|
+
options.printLine(columns.map((c) => c.header).join(','));
|
|
143
|
+
}
|
|
144
|
+
for (const d of data) {
|
|
145
|
+
const row = this.getCSVRow(d);
|
|
146
|
+
options.printLine(row.join(','));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
outputTable() {
|
|
150
|
+
// tslint:disable-next-line:no-this-assignment
|
|
151
|
+
const { data, columns, options } = this;
|
|
152
|
+
// column truncation
|
|
153
|
+
//
|
|
154
|
+
// find max width for each column
|
|
155
|
+
for (const col of columns) {
|
|
156
|
+
// convert multi-line cell to single longest line
|
|
157
|
+
// for width calculations
|
|
158
|
+
const widthData = data.map((row) => {
|
|
159
|
+
const d = row[col.key];
|
|
160
|
+
const manyLines = d.split('\n');
|
|
161
|
+
if (manyLines.length > 1) {
|
|
162
|
+
return '*'.repeat(Math.max(...manyLines.map((r) => sw(r))));
|
|
163
|
+
}
|
|
164
|
+
return d;
|
|
165
|
+
});
|
|
166
|
+
const widths = ['.'.padEnd(col.minWidth - 1), col.header, ...widthData.map((row) => row)].map((r) => sw(r));
|
|
167
|
+
col.maxWidth = Math.max(...widths) + 1;
|
|
168
|
+
col.width = col.maxWidth;
|
|
169
|
+
}
|
|
170
|
+
// terminal width
|
|
171
|
+
const maxWidth = screen_1.stdtermwidth - 2;
|
|
172
|
+
// truncation logic
|
|
173
|
+
const shouldShorten = () => {
|
|
174
|
+
// don't shorten if full mode
|
|
175
|
+
if (options['no-truncate'] || (!process.stdout.isTTY && !process.env.CLI_UX_SKIP_TTY_CHECK))
|
|
176
|
+
return;
|
|
177
|
+
// don't shorten if there is enough screen width
|
|
178
|
+
const dataMaxWidth = _.sumBy(columns, (c) => c.width);
|
|
179
|
+
const overWidth = dataMaxWidth - maxWidth;
|
|
180
|
+
if (overWidth <= 0)
|
|
181
|
+
return;
|
|
182
|
+
// not enough room, short all columns to minWidth
|
|
183
|
+
for (const col of columns) {
|
|
184
|
+
col.width = col.minWidth;
|
|
185
|
+
}
|
|
186
|
+
// if sum(minWidth's) is greater than term width
|
|
187
|
+
// nothing can be done so
|
|
188
|
+
// display all as minWidth
|
|
189
|
+
const dataMinWidth = _.sumBy(columns, (c) => c.minWidth);
|
|
190
|
+
if (dataMinWidth >= maxWidth)
|
|
191
|
+
return;
|
|
192
|
+
// some wiggle room left, add it back to "needy" columns
|
|
193
|
+
let wiggleRoom = maxWidth - dataMinWidth;
|
|
194
|
+
const needyCols = columns.map((c) => ({ key: c.key, needs: c.maxWidth - c.width })).sort((a, b) => a.needs - b.needs);
|
|
195
|
+
for (const { key, needs } of needyCols) {
|
|
196
|
+
if (!needs)
|
|
197
|
+
continue;
|
|
198
|
+
const col = columns.find((c) => key === c.key);
|
|
199
|
+
if (!col)
|
|
200
|
+
continue;
|
|
201
|
+
if (wiggleRoom > needs) {
|
|
202
|
+
col.width = col.width + needs;
|
|
203
|
+
wiggleRoom -= needs;
|
|
204
|
+
}
|
|
205
|
+
else if (wiggleRoom) {
|
|
206
|
+
col.width = col.width + wiggleRoom;
|
|
207
|
+
wiggleRoom = 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
shouldShorten();
|
|
212
|
+
// print table title
|
|
213
|
+
if (options.title) {
|
|
214
|
+
options.printLine(options.title);
|
|
215
|
+
// print title divider
|
|
216
|
+
options.printLine(''.padEnd(columns.reduce((sum, col) => sum + col.width, 1), '='));
|
|
217
|
+
options.rowStart = '| ';
|
|
218
|
+
}
|
|
219
|
+
// print headers
|
|
220
|
+
if (!options['no-header']) {
|
|
221
|
+
let headers = options.rowStart;
|
|
222
|
+
for (const col of columns) {
|
|
223
|
+
const header = col.header;
|
|
224
|
+
headers += header.padEnd(col.width);
|
|
225
|
+
}
|
|
226
|
+
options.printLine(chalk.bold(headers));
|
|
227
|
+
// print header dividers
|
|
228
|
+
let dividers = options.rowStart;
|
|
229
|
+
for (const col of columns) {
|
|
230
|
+
const divider = ''.padEnd(col.width - 1, '─') + ' ';
|
|
231
|
+
dividers += divider.padEnd(col.width);
|
|
232
|
+
}
|
|
233
|
+
options.printLine(chalk.bold(dividers));
|
|
234
|
+
}
|
|
235
|
+
// print rows
|
|
236
|
+
for (const row of data) {
|
|
237
|
+
// find max number of lines
|
|
238
|
+
// for all cells in a row
|
|
239
|
+
// with multi-line strings
|
|
240
|
+
let numOfLines = 1;
|
|
241
|
+
for (const col of columns) {
|
|
242
|
+
const d = row[col.key];
|
|
243
|
+
const lines = d.split('\n').length;
|
|
244
|
+
if (lines > numOfLines)
|
|
245
|
+
numOfLines = lines;
|
|
246
|
+
}
|
|
247
|
+
// eslint-disable-next-line unicorn/no-new-array
|
|
248
|
+
const linesIndexess = [...new Array(numOfLines).keys()];
|
|
249
|
+
// print row
|
|
250
|
+
// including multi-lines
|
|
251
|
+
for (const i of linesIndexess) {
|
|
252
|
+
let l = options.rowStart;
|
|
253
|
+
for (const col of columns) {
|
|
254
|
+
const width = col.width;
|
|
255
|
+
let d = row[col.key];
|
|
256
|
+
d = d.split('\n')[i] || '';
|
|
257
|
+
const visualWidth = sw(d);
|
|
258
|
+
const colorWidth = d.length - visualWidth;
|
|
259
|
+
let cell = d.padEnd(width + colorWidth);
|
|
260
|
+
if (cell.length - colorWidth > width || visualWidth === width) {
|
|
261
|
+
cell = cell.slice(0, width - 2) + '… ';
|
|
262
|
+
}
|
|
263
|
+
l += cell;
|
|
264
|
+
}
|
|
265
|
+
options.printLine(l);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function table(data, columns, options = {}) {
|
|
271
|
+
new Table(data, columns, options).display();
|
|
272
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function colorizeByStatus(text: string | unknown, status: string): string | unknown;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.colorizeByStatus = colorizeByStatus;
|
|
4
|
+
const color_1 = require("@oclif/color");
|
|
5
|
+
function colorizeByStatus(text, status) {
|
|
6
|
+
return status.includes('removed') || status.includes('sensitivity-changed') ? color_1.default.red(text) : text;
|
|
7
|
+
}
|