bunosh 0.1.4 ā 0.2.2
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/COMPLETION.md +214 -0
- package/README.md +489 -73
- package/UPGRADE.md +200 -0
- package/bunosh.js +56 -0
- package/index.js +22 -11
- package/package.json +32 -24
- package/src/completion.js +341 -0
- package/src/font.js +258 -0
- package/src/formatters/base.js +17 -0
- package/src/formatters/console.js +81 -0
- package/src/formatters/factory.js +17 -0
- package/src/formatters/github-actions.js +43 -0
- package/src/init.js +13 -6
- package/src/io.js +20 -0
- package/src/printer.js +91 -0
- package/src/program.js +374 -154
- package/src/task.js +148 -0
- package/src/tasks/copyFile.js +21 -0
- package/src/tasks/exec.js +204 -0
- package/src/tasks/fetch.js +47 -0
- package/src/tasks/{writeToFile.jsx ā writeToFile.js} +18 -16
- package/src/upgrade.js +255 -0
- package/types.d.ts +44 -0
- package/run.js +0 -31
- package/src/io.jsx +0 -47
- package/src/output.js +0 -37
- package/src/task.jsx +0 -209
- package/src/tasks/copyFile.jsx +0 -14
- package/src/tasks/exec.jsx +0 -104
- package/src/tasks/fetch.jsx +0 -74
- package/templates/banner.js +0 -8
package/src/task.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
|
+
import Printer from './printer.js';
|
|
3
|
+
|
|
4
|
+
export const TaskStatus = {
|
|
5
|
+
RUNNING: 'running',
|
|
6
|
+
FAIL: 'fail',
|
|
7
|
+
SUCCESS: 'success'
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const tasksExecuted = [];
|
|
11
|
+
export const runningTasks = new Map();
|
|
12
|
+
|
|
13
|
+
let taskCounter = 0;
|
|
14
|
+
let stopFailToggle = true;
|
|
15
|
+
const asyncLocalStorage = new AsyncLocalStorage();
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export function stopOnFail(enable = true) {
|
|
19
|
+
stopFailToggle = enable;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ignoreFail(enable = true) {
|
|
23
|
+
stopFailToggle = !enable;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
|
|
28
|
+
process.on('exit', (code) => {
|
|
29
|
+
if (!process.env.BUNOSH_COMMAND_STARTED) return;
|
|
30
|
+
|
|
31
|
+
const totalTime = Date.now() - startTime;
|
|
32
|
+
const success = code === 0;
|
|
33
|
+
const tasksFailed = tasksExecuted.filter(ti => ti.result?.status === TaskStatus.FAIL).length;
|
|
34
|
+
|
|
35
|
+
console.log(`\nš² ${success ? '' : 'FAIL '}Exit Code: ${code} | Tasks: ${tasksExecuted.length}${tasksFailed ? ` | Failed: ${tasksFailed}` : ''} | Time: ${totalTime}ms`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export function getRunningTaskCount() {
|
|
39
|
+
return runningTasks.size;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getCurrentTaskId() {
|
|
43
|
+
return asyncLocalStorage.getStore();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getTaskPrefix(taskId) {
|
|
47
|
+
const taskNumber = Array.from(runningTasks.keys()).indexOf(taskId) + 1;
|
|
48
|
+
return getRunningTaskCount() > 1 ? `ā°${taskNumber}ā±` : '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createTaskInfo(name) {
|
|
52
|
+
const taskInfo = new TaskInfo(name, Date.now(), TaskStatus.RUNNING);
|
|
53
|
+
runningTasks.set(taskInfo.id, taskInfo);
|
|
54
|
+
tasksExecuted.push(taskInfo);
|
|
55
|
+
return taskInfo;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function finishTaskInfo(taskInfo, success = true, error = null, output = null) {
|
|
59
|
+
const endTime = Date.now();
|
|
60
|
+
const duration = endTime - taskInfo.startTime;
|
|
61
|
+
|
|
62
|
+
taskInfo.status = success ? TaskStatus.SUCCESS : TaskStatus.FAIL;
|
|
63
|
+
taskInfo.duration = duration;
|
|
64
|
+
taskInfo.result = {
|
|
65
|
+
status: success ? TaskStatus.SUCCESS : TaskStatus.FAIL,
|
|
66
|
+
output: error?.message || output || null
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
runningTasks.delete(taskInfo.id);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class TaskInfo {
|
|
73
|
+
constructor(name, startTime, status) {
|
|
74
|
+
this.id = `task-${++taskCounter}-${Math.random().toString(36).substring(7)}`;
|
|
75
|
+
this.name = name;
|
|
76
|
+
this.startTime = startTime;
|
|
77
|
+
this.status = status;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function task(name, fn) {
|
|
82
|
+
if (!fn) {
|
|
83
|
+
fn = name;
|
|
84
|
+
name = fn.toString().slice(0, 50).replace(/\s+/g, ' ').trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const taskInfo = new TaskInfo(name, Date.now(), TaskStatus.RUNNING);
|
|
88
|
+
|
|
89
|
+
tasksExecuted.push(taskInfo);
|
|
90
|
+
runningTasks.set(taskInfo.id, taskInfo);
|
|
91
|
+
|
|
92
|
+
const printer = new Printer('task', taskInfo.id);
|
|
93
|
+
printer.start(name);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const result = await asyncLocalStorage.run(taskInfo.id, async () => {
|
|
97
|
+
return await Promise.resolve(fn());
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const endTime = Date.now();
|
|
101
|
+
const duration = endTime - taskInfo.startTime;
|
|
102
|
+
|
|
103
|
+
taskInfo.status = TaskStatus.SUCCESS;
|
|
104
|
+
taskInfo.duration = duration;
|
|
105
|
+
taskInfo.result = { status: TaskStatus.SUCCESS, output: result };
|
|
106
|
+
|
|
107
|
+
printer.finish(name);
|
|
108
|
+
runningTasks.delete(taskInfo.id);
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
const endTime = Date.now();
|
|
113
|
+
const duration = endTime - taskInfo.startTime;
|
|
114
|
+
|
|
115
|
+
taskInfo.status = TaskStatus.FAIL;
|
|
116
|
+
taskInfo.duration = duration;
|
|
117
|
+
taskInfo.result = { status: TaskStatus.FAIL, output: err.message };
|
|
118
|
+
|
|
119
|
+
printer.error(name, err);
|
|
120
|
+
runningTasks.delete(taskInfo.id);
|
|
121
|
+
|
|
122
|
+
// Don't exit during testing
|
|
123
|
+
const isTestEnvironment = process.env.NODE_ENV === 'test' ||
|
|
124
|
+
typeof Bun?.jest !== 'undefined' ||
|
|
125
|
+
process.argv.some(arg => arg.includes('test'));
|
|
126
|
+
|
|
127
|
+
if (stopFailToggle && !isTestEnvironment) {
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class TaskResult {
|
|
136
|
+
constructor({ status, output }) {
|
|
137
|
+
this.status = status;
|
|
138
|
+
this.output = output;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static fail(output = null) {
|
|
142
|
+
return new TaskResult({ status: TaskStatus.FAIL, output });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static success(output = null) {
|
|
146
|
+
return new TaskResult({ status: TaskStatus.SUCCESS, output });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fsExtra from 'fs-extra';
|
|
2
|
+
const { copySync } = fsExtra;
|
|
3
|
+
import Printer from '../printer.js';
|
|
4
|
+
import { createTaskInfo, finishTaskInfo } from '../task.js';
|
|
5
|
+
|
|
6
|
+
export default function copyFile(src, dst) {
|
|
7
|
+
const taskName = `${src} ā ${dst}`;
|
|
8
|
+
const taskInfo = createTaskInfo(taskName);
|
|
9
|
+
const printer = new Printer('copyFile', taskInfo.id);
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
printer.start(taskName);
|
|
13
|
+
copySync(src, dst);
|
|
14
|
+
printer.finish(taskName);
|
|
15
|
+
finishTaskInfo(taskInfo, true, null, 'File copied');
|
|
16
|
+
} catch (error) {
|
|
17
|
+
printer.error(taskName, error);
|
|
18
|
+
finishTaskInfo(taskInfo, false, error, error.message);
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { TaskResult, createTaskInfo, finishTaskInfo } from '../task.js';
|
|
2
|
+
import Printer from '../printer.js';
|
|
3
|
+
|
|
4
|
+
const isBun = typeof Bun !== 'undefined';
|
|
5
|
+
|
|
6
|
+
export default function exec(strings, ...values) {
|
|
7
|
+
const cmd = strings.reduce((accumulator, str, i) => {
|
|
8
|
+
return accumulator + str + (values[i] || '');
|
|
9
|
+
}, '');
|
|
10
|
+
|
|
11
|
+
let envs = null;
|
|
12
|
+
let cwd = null;
|
|
13
|
+
|
|
14
|
+
const cmdPromise = new Promise(async (resolve, reject) => {
|
|
15
|
+
const extraInfo = {};
|
|
16
|
+
if (cwd) extraInfo.cwd = cwd;
|
|
17
|
+
if (envs) extraInfo.env = envs;
|
|
18
|
+
|
|
19
|
+
const taskInfo = createTaskInfo(cmd);
|
|
20
|
+
const printer = new Printer('exec', taskInfo.id);
|
|
21
|
+
printer.start(cmd, extraInfo);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
if (global.disableBunForTesting || !isBun) {
|
|
25
|
+
const result = await nodeExec(cmd, extraInfo, printer, taskInfo);
|
|
26
|
+
if (result.status === 'success') {
|
|
27
|
+
finishTaskInfo(taskInfo, true, null, result.output);
|
|
28
|
+
resolve(result);
|
|
29
|
+
} else {
|
|
30
|
+
finishTaskInfo(taskInfo, false, new Error(result.output), result.output);
|
|
31
|
+
resolve(result);
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Bun implementation with real-time streaming
|
|
37
|
+
const needsShell = cmd.includes('|') || cmd.includes('>') || cmd.includes('<') || cmd.includes('&&') || cmd.includes('||') || cmd.includes("'") || cmd.includes('"') || cmd.includes(';');
|
|
38
|
+
|
|
39
|
+
const { spawn } = Bun;
|
|
40
|
+
const proc = spawn({
|
|
41
|
+
cmd: needsShell ? ['/bin/sh', '-c', cmd] : cmd.trim().split(/\s+/),
|
|
42
|
+
cwd: cwd || process.cwd(),
|
|
43
|
+
env: {
|
|
44
|
+
...(envs ? { ...process.env, ...envs } : process.env),
|
|
45
|
+
},
|
|
46
|
+
stdout: "pipe",
|
|
47
|
+
stderr: "pipe",
|
|
48
|
+
stdin: "ignore"
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const decoder = new TextDecoder();
|
|
52
|
+
let output = '';
|
|
53
|
+
let finished = false;
|
|
54
|
+
|
|
55
|
+
// Process stdout
|
|
56
|
+
const readStdout = async () => {
|
|
57
|
+
const reader = proc.stdout.getReader();
|
|
58
|
+
let buffer = '';
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
while (!finished) {
|
|
62
|
+
const { done, value } = await reader.read();
|
|
63
|
+
if (done) break;
|
|
64
|
+
|
|
65
|
+
const text = decoder.decode(value, { stream: true });
|
|
66
|
+
buffer += text;
|
|
67
|
+
|
|
68
|
+
const lines = buffer.split('\n');
|
|
69
|
+
buffer = lines.pop();
|
|
70
|
+
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
if (line.trim()) {
|
|
73
|
+
printer.output(line);
|
|
74
|
+
output += line + '\n';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (buffer.trim()) {
|
|
80
|
+
printer.output(buffer);
|
|
81
|
+
output += buffer + '\n';
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
reader.releaseLock();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Process stderr
|
|
89
|
+
const readStderr = async () => {
|
|
90
|
+
const reader = proc.stderr.getReader();
|
|
91
|
+
let buffer = '';
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
while (!finished) {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done) break;
|
|
97
|
+
|
|
98
|
+
const text = decoder.decode(value, { stream: true });
|
|
99
|
+
buffer += text;
|
|
100
|
+
|
|
101
|
+
const lines = buffer.split('\n');
|
|
102
|
+
buffer = lines.pop();
|
|
103
|
+
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (line.trim()) {
|
|
106
|
+
printer.output(line, true);
|
|
107
|
+
output += line + '\n';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (buffer.trim()) {
|
|
113
|
+
printer.output(buffer, true);
|
|
114
|
+
output += buffer + '\n';
|
|
115
|
+
}
|
|
116
|
+
} finally {
|
|
117
|
+
reader.releaseLock();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Start reading both streams
|
|
122
|
+
const [, , exitResult] = await Promise.all([
|
|
123
|
+
readStdout(),
|
|
124
|
+
readStderr(),
|
|
125
|
+
proc.exited
|
|
126
|
+
]);
|
|
127
|
+
|
|
128
|
+
finished = true;
|
|
129
|
+
const exitCode = parseInt(exitResult, 10);
|
|
130
|
+
|
|
131
|
+
if (exitCode === 0) {
|
|
132
|
+
printer.finish(cmd);
|
|
133
|
+
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
134
|
+
resolve(TaskResult.success(output.trim()));
|
|
135
|
+
} else {
|
|
136
|
+
const error = new Error(`Exit code: ${exitCode}`);
|
|
137
|
+
printer.error(cmd, null, { exitCode });
|
|
138
|
+
finishTaskInfo(taskInfo, false, error, output.trim());
|
|
139
|
+
resolve(TaskResult.fail(output.trim()));
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
printer.error(cmd, error);
|
|
143
|
+
finishTaskInfo(taskInfo, false, error, error.message);
|
|
144
|
+
resolve(TaskResult.fail(error.message));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
cmdPromise.env = (newEnvs) => {
|
|
149
|
+
envs = newEnvs;
|
|
150
|
+
return cmdPromise;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
cmdPromise.cwd = (newCwd) => {
|
|
154
|
+
cwd = newCwd;
|
|
155
|
+
return cmdPromise;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return cmdPromise;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function nodeExec(cmd, extraInfo, printer, taskInfo) {
|
|
162
|
+
// Node.js fallback - simple execution without real-time output
|
|
163
|
+
const { spawn } = await import('child_process');
|
|
164
|
+
|
|
165
|
+
return new Promise((resolve) => {
|
|
166
|
+
const proc = spawn('sh', ['-c', cmd], {
|
|
167
|
+
cwd: extraInfo.cwd || process.cwd(),
|
|
168
|
+
env: extraInfo.env ? { ...process.env, ...extraInfo.env } : process.env,
|
|
169
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
let output = '';
|
|
173
|
+
let errorOutput = '';
|
|
174
|
+
|
|
175
|
+
proc.stdout.on('data', (data) => {
|
|
176
|
+
const text = data.toString();
|
|
177
|
+
printer.output(text.trim());
|
|
178
|
+
output += text;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
proc.stderr.on('data', (data) => {
|
|
182
|
+
const text = data.toString();
|
|
183
|
+
printer.output(text.trim(), true);
|
|
184
|
+
errorOutput += text;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
proc.on('close', (code) => {
|
|
188
|
+
const combinedOutput = (output + errorOutput).trim();
|
|
189
|
+
|
|
190
|
+
if (code === 0) {
|
|
191
|
+
printer.finish(cmd);
|
|
192
|
+
resolve(TaskResult.success(combinedOutput));
|
|
193
|
+
} else {
|
|
194
|
+
printer.error(cmd, new Error(`Exit code: ${code}`));
|
|
195
|
+
resolve(TaskResult.fail(combinedOutput));
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
proc.on('error', (error) => {
|
|
200
|
+
printer.error(cmd, error);
|
|
201
|
+
resolve(TaskResult.fail(error.message));
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { TaskResult, createTaskInfo, finishTaskInfo } from '../task.js';
|
|
2
|
+
import Printer from '../printer.js';
|
|
3
|
+
|
|
4
|
+
export default async function httpFetch() {
|
|
5
|
+
const url = arguments[0];
|
|
6
|
+
const method = arguments[1]?.method || 'GET';
|
|
7
|
+
const taskName = `${method} ${url}`;
|
|
8
|
+
|
|
9
|
+
const taskInfo = createTaskInfo(taskName);
|
|
10
|
+
const printer = new Printer('fetch', taskInfo.id);
|
|
11
|
+
printer.start(taskName);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(...arguments);
|
|
15
|
+
const textDecoder = new TextDecoder();
|
|
16
|
+
let output = '';
|
|
17
|
+
|
|
18
|
+
if (response.body) {
|
|
19
|
+
for await (const chunk of response.body) {
|
|
20
|
+
const lines = textDecoder.decode(chunk, { stream: true }).toString().split('\n');
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
if (line.trim()) {
|
|
23
|
+
printer.print(line, 'output');
|
|
24
|
+
output += line + '\n';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (response.ok) {
|
|
31
|
+
printer.finish(taskName, { status: `${response.status} ${response.statusText}` });
|
|
32
|
+
finishTaskInfo(taskInfo, true, null, output.trim());
|
|
33
|
+
return TaskResult.success(output.trim());
|
|
34
|
+
} else {
|
|
35
|
+
const errorMsg = `HTTP ${response.status} ${response.statusText}`;
|
|
36
|
+
const error = new Error(errorMsg);
|
|
37
|
+
printer.error(taskName, errorMsg);
|
|
38
|
+
finishTaskInfo(taskInfo, false, error, errorMsg);
|
|
39
|
+
return TaskResult.fail(errorMsg);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
} catch (error) {
|
|
43
|
+
printer.error(taskName, error);
|
|
44
|
+
finishTaskInfo(taskInfo, false, error, error.message);
|
|
45
|
+
return TaskResult.fail(error.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
-
import
|
|
3
|
+
import Printer from '../printer.js';
|
|
4
|
+
import { createTaskInfo, finishTaskInfo } from '../task.js';
|
|
4
5
|
|
|
5
6
|
export default function writeToFile(fileName, lineBuilderFn) {
|
|
6
|
-
|
|
7
7
|
let text = '';
|
|
8
8
|
|
|
9
9
|
const fileLine = function(strings, ...values) {
|
|
@@ -13,7 +13,6 @@ export default function writeToFile(fileName, lineBuilderFn) {
|
|
|
13
13
|
}, '');
|
|
14
14
|
text += `${line}\n`;
|
|
15
15
|
return;
|
|
16
|
-
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
text += `${strings}\n`;
|
|
@@ -27,23 +26,26 @@ export default function writeToFile(fileName, lineBuilderFn) {
|
|
|
27
26
|
text += fs.readFileSync(path.join(process.cwd(), fileName));
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const taskInfo = createTaskInfo(fileName);
|
|
30
|
+
const printer = new Printer('writeToFile', taskInfo.id);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
printer.start(fileName);
|
|
34
|
+
|
|
32
35
|
if (lineBuilderFn instanceof Function) {
|
|
33
36
|
lineBuilderFn(fileLine);
|
|
34
37
|
} else {
|
|
35
38
|
text += lineBuilderFn;
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
fs.writeFileSync(fileName, text);
|
|
42
|
+
printer.finish(fileName, { characters: text.length });
|
|
43
|
+
finishTaskInfo(taskInfo, true, null, `${text.length} characters`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
printer.error(fileName, error);
|
|
46
|
+
finishTaskInfo(taskInfo, false, error, error.message);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
43
49
|
|
|
44
50
|
return text;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
}
|