bunosh 0.4.14 → 0.5.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 +93 -565
- package/bunosh.js +33 -155
- package/package.json +1 -1
- package/src/error-formatter.js +80 -0
- package/src/program.js +85 -331
package/bunosh.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
process.removeAllListeners('warning');
|
|
4
|
+
process.on('warning', () => {});
|
|
2
5
|
|
|
3
|
-
// Set up global variables BEFORE any imports
|
|
4
6
|
globalThis._bunoshStartTime = Date.now();
|
|
5
7
|
globalThis._bunoshCommandCompleted = false;
|
|
6
8
|
globalThis._bunoshGlobalTasksExecuted = [];
|
|
@@ -12,35 +14,28 @@ globalThis._bunoshGlobalTaskStatus = {
|
|
|
12
14
|
WARNING: 'warning'
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
// Now import modules
|
|
16
|
-
|
|
17
17
|
import bunosh, { BUNOSHFILE, banner } from "./src/program.js";
|
|
18
18
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
19
19
|
import init from "./src/init.js";
|
|
20
20
|
import path from "path";
|
|
21
21
|
import { config } from "dotenv";
|
|
22
|
+
import { formatError } from "./src/error-formatter.js";
|
|
23
|
+
import color from "chalk";
|
|
22
24
|
|
|
23
|
-
/**
|
|
24
|
-
* Load environment variables from .env files following Bun's loading order
|
|
25
|
-
* @param {string} bunoshfileDir - Directory containing the Bunoshfile
|
|
26
|
-
* @param {string} customEnvFile - Optional custom env file path from --env-file option
|
|
27
|
-
*/
|
|
28
25
|
function loadEnvFiles(bunoshfileDir, customEnvFile = null) {
|
|
29
26
|
if (customEnvFile) {
|
|
30
|
-
// Load the specified env file
|
|
31
27
|
const customEnvPath = path.isAbsolute(customEnvFile)
|
|
32
28
|
? customEnvFile
|
|
33
29
|
: path.resolve(bunoshfileDir, customEnvFile);
|
|
34
30
|
|
|
35
31
|
if (existsSync(customEnvPath)) {
|
|
36
|
-
config({ path: customEnvPath });
|
|
32
|
+
config({ path: customEnvPath, quiet: true });
|
|
37
33
|
} else {
|
|
38
34
|
console.warn(`Warning: Specified env file not found: ${customEnvPath}`);
|
|
39
35
|
}
|
|
40
36
|
return;
|
|
41
37
|
}
|
|
42
38
|
|
|
43
|
-
// Follow Bun's automatic loading order
|
|
44
39
|
const envFiles = [
|
|
45
40
|
'.env',
|
|
46
41
|
'.env.production',
|
|
@@ -52,7 +47,7 @@ function loadEnvFiles(bunoshfileDir, customEnvFile = null) {
|
|
|
52
47
|
envFiles.forEach(envFile => {
|
|
53
48
|
const envPath = path.join(bunoshfileDir, envFile);
|
|
54
49
|
if (existsSync(envPath)) {
|
|
55
|
-
config({ path: envPath });
|
|
50
|
+
config({ path: envPath, quiet: true });
|
|
56
51
|
}
|
|
57
52
|
});
|
|
58
53
|
}
|
|
@@ -60,38 +55,32 @@ function loadEnvFiles(bunoshfileDir, customEnvFile = null) {
|
|
|
60
55
|
async function loadBunoshfiles(tasksFile) {
|
|
61
56
|
const path = await import('path');
|
|
62
57
|
const fs = await import('fs');
|
|
63
|
-
|
|
64
|
-
// Get the directory and base name of the tasks file
|
|
58
|
+
|
|
65
59
|
const dir = path.dirname(tasksFile);
|
|
66
60
|
const baseName = path.basename(tasksFile, '.js');
|
|
67
|
-
|
|
68
|
-
// Find all matching Bunoshfile variants
|
|
61
|
+
|
|
69
62
|
const files = fs.readdirSync(dir);
|
|
70
63
|
const bunoshFiles = files
|
|
71
64
|
.filter(file => {
|
|
72
|
-
// Match Bunoshfile.js, Bunoshfile.*.js
|
|
73
65
|
const regex = new RegExp(`^${baseName}(\\.\\w+)?\\.js$`);
|
|
74
66
|
return regex.test(file);
|
|
75
67
|
})
|
|
76
|
-
.sort();
|
|
77
|
-
|
|
68
|
+
.sort();
|
|
69
|
+
|
|
78
70
|
const allTasks = {};
|
|
79
71
|
const allSources = {};
|
|
80
|
-
|
|
72
|
+
|
|
81
73
|
for (const file of bunoshFiles) {
|
|
82
74
|
const filePath = path.join(dir, file);
|
|
83
75
|
try {
|
|
84
|
-
// Extract namespace from filename
|
|
85
76
|
let namespace = '';
|
|
86
77
|
if (file !== `${baseName}.js`) {
|
|
87
|
-
// Remove baseName and .js, then remove the leading dot
|
|
88
78
|
namespace = file.slice(baseName.length, -3).substring(1);
|
|
89
79
|
}
|
|
90
|
-
|
|
80
|
+
|
|
91
81
|
const tasks = await import(filePath);
|
|
92
82
|
const source = fs.readFileSync(filePath, 'utf-8');
|
|
93
|
-
|
|
94
|
-
// Add namespace prefix to tasks if namespace exists
|
|
83
|
+
|
|
95
84
|
if (namespace) {
|
|
96
85
|
Object.keys(tasks).forEach(key => {
|
|
97
86
|
if (typeof tasks[key] === 'function') {
|
|
@@ -100,7 +89,6 @@ async function loadBunoshfiles(tasksFile) {
|
|
|
100
89
|
}
|
|
101
90
|
});
|
|
102
91
|
} else {
|
|
103
|
-
// No namespace for the main Bunoshfile
|
|
104
92
|
Object.keys(tasks).forEach(key => {
|
|
105
93
|
if (typeof tasks[key] === 'function') {
|
|
106
94
|
allTasks[key] = tasks[key];
|
|
@@ -112,51 +100,42 @@ async function loadBunoshfiles(tasksFile) {
|
|
|
112
100
|
console.warn(`Warning: Could not load ${file}:`, error.message);
|
|
113
101
|
}
|
|
114
102
|
}
|
|
115
|
-
|
|
103
|
+
|
|
116
104
|
return { tasks: allTasks, sources: allSources };
|
|
117
105
|
}
|
|
118
106
|
|
|
119
107
|
async function main() {
|
|
120
108
|
|
|
121
|
-
// Parse --bunoshfile flag or BUNOSHFILE env var before importing tasks
|
|
122
109
|
const bunoshfileIndex = process.argv.indexOf('--bunoshfile');
|
|
123
110
|
let customBunoshfile = null;
|
|
124
111
|
|
|
125
112
|
if (bunoshfileIndex !== -1 && bunoshfileIndex + 1 < process.argv.length) {
|
|
126
113
|
customBunoshfile = process.argv[bunoshfileIndex + 1];
|
|
127
|
-
// Remove the flag and its value from process.argv so it doesn't interfere with command parsing
|
|
128
114
|
process.argv.splice(bunoshfileIndex, 2);
|
|
129
115
|
} else if (process.env.BUNOSHFILE) {
|
|
130
116
|
customBunoshfile = process.env.BUNOSHFILE;
|
|
131
117
|
}
|
|
132
118
|
|
|
133
|
-
// Parse --env-file flag
|
|
134
119
|
const envFileIndex = process.argv.findIndex(arg => arg.startsWith('--env-file='));
|
|
135
120
|
let customEnvFile = null;
|
|
136
121
|
|
|
137
122
|
if (envFileIndex !== -1) {
|
|
138
123
|
customEnvFile = process.argv[envFileIndex].split('=')[1];
|
|
139
|
-
// Remove the flag from process.argv so it doesn't interfere with command parsing
|
|
140
124
|
process.argv.splice(envFileIndex, 1);
|
|
141
125
|
}
|
|
142
126
|
|
|
143
|
-
// Check for -mcp flag first
|
|
144
127
|
const mcpFlagIndex = process.argv.indexOf('-mcp');
|
|
145
128
|
if (mcpFlagIndex !== -1) {
|
|
146
|
-
// Remove the flag from process.argv
|
|
147
129
|
process.argv.splice(mcpFlagIndex, 1);
|
|
148
130
|
|
|
149
|
-
// Set environment variable to indicate MCP mode
|
|
150
131
|
process.env.BUNOSH_MCP_MODE = 'true';
|
|
151
132
|
|
|
152
|
-
// Import MCP server and start it
|
|
153
133
|
const { createMcpServer, startMcpServer } = await import('./src/mcp-server.js');
|
|
154
134
|
|
|
155
135
|
let tasksFile;
|
|
156
136
|
let bunoshfileDir;
|
|
157
137
|
if (customBunoshfile) {
|
|
158
138
|
const resolvedPath = path.isAbsolute(customBunoshfile) ? customBunoshfile : path.resolve(customBunoshfile);
|
|
159
|
-
// If it's a directory, append the default BUNOSHFILE
|
|
160
139
|
if (existsSync(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
161
140
|
tasksFile = path.join(resolvedPath, BUNOSHFILE);
|
|
162
141
|
bunoshfileDir = resolvedPath;
|
|
@@ -174,33 +153,27 @@ async function main() {
|
|
|
174
153
|
process.exit(1);
|
|
175
154
|
}
|
|
176
155
|
|
|
177
|
-
// Load environment files from the Bunoshfile directory
|
|
178
156
|
loadEnvFiles(bunoshfileDir, customEnvFile);
|
|
179
157
|
|
|
180
|
-
// Load tasks and sources
|
|
181
158
|
const { tasks, sources } = await loadBunoshfiles(tasksFile);
|
|
182
159
|
|
|
183
|
-
// Create and start MCP server
|
|
184
160
|
const server = createMcpServer(tasks, sources);
|
|
185
161
|
await startMcpServer(server);
|
|
186
162
|
|
|
187
|
-
return;
|
|
163
|
+
return;
|
|
188
164
|
}
|
|
189
165
|
|
|
190
166
|
let tasksFile;
|
|
191
167
|
let bunoshfileDir;
|
|
192
168
|
if (customBunoshfile) {
|
|
193
169
|
const resolvedPath = path.isAbsolute(customBunoshfile) ? customBunoshfile : path.resolve(customBunoshfile);
|
|
194
|
-
// If it's a directory, append the default BUNOSHFILE
|
|
195
170
|
if (existsSync(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
196
171
|
tasksFile = path.join(resolvedPath, BUNOSHFILE);
|
|
197
172
|
bunoshfileDir = resolvedPath;
|
|
198
|
-
// Change working directory to the bunoshfile directory
|
|
199
173
|
process.chdir(resolvedPath);
|
|
200
174
|
} else {
|
|
201
175
|
tasksFile = resolvedPath;
|
|
202
176
|
bunoshfileDir = path.dirname(resolvedPath);
|
|
203
|
-
// Change working directory to the bunoshfile's directory
|
|
204
177
|
process.chdir(path.dirname(resolvedPath));
|
|
205
178
|
}
|
|
206
179
|
} else {
|
|
@@ -208,44 +181,37 @@ async function main() {
|
|
|
208
181
|
bunoshfileDir = process.cwd();
|
|
209
182
|
}
|
|
210
183
|
|
|
211
|
-
// Load environment files from the Bunoshfile directory
|
|
212
184
|
loadEnvFiles(bunoshfileDir, customEnvFile);
|
|
213
185
|
|
|
214
|
-
// Handle -e flag for executing JavaScript code
|
|
215
186
|
const eFlagIndex = process.argv.indexOf('-e');
|
|
216
187
|
if (eFlagIndex !== -1) {
|
|
217
188
|
(async () => {
|
|
218
189
|
let jsCode = '';
|
|
219
|
-
|
|
220
|
-
// Check if code is provided as argument
|
|
190
|
+
|
|
221
191
|
if (eFlagIndex + 1 < process.argv.length && !process.argv[eFlagIndex + 1].startsWith('-')) {
|
|
222
192
|
jsCode = process.argv[eFlagIndex + 1];
|
|
223
193
|
} else if (!process.stdin.isTTY) {
|
|
224
|
-
// Read from stdin only if it's not a TTY (i.e., it's being piped)
|
|
225
194
|
const chunks = [];
|
|
226
195
|
for await (const chunk of process.stdin) {
|
|
227
196
|
chunks.push(chunk);
|
|
228
197
|
}
|
|
229
198
|
jsCode = Buffer.concat(chunks).toString('utf8').trim();
|
|
230
199
|
}
|
|
231
|
-
|
|
200
|
+
|
|
232
201
|
if (!jsCode) {
|
|
233
202
|
console.error('No JavaScript code provided');
|
|
234
203
|
process.exit(1);
|
|
235
204
|
}
|
|
236
|
-
|
|
205
|
+
|
|
237
206
|
try {
|
|
238
|
-
// Import bunosh globals before executing JavaScript
|
|
239
207
|
await import('./index.js');
|
|
240
|
-
|
|
241
|
-
// Make bunosh globals available to the function
|
|
208
|
+
|
|
242
209
|
for (const [key, value] of Object.entries(global.bunosh)) {
|
|
243
210
|
if (typeof value === 'function') {
|
|
244
211
|
globalThis[key] = value;
|
|
245
212
|
}
|
|
246
213
|
}
|
|
247
|
-
|
|
248
|
-
// Execute the JavaScript code with bunosh globals available
|
|
214
|
+
|
|
249
215
|
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
250
216
|
const func = new AsyncFunction(jsCode);
|
|
251
217
|
await func();
|
|
@@ -268,7 +234,7 @@ async function main() {
|
|
|
268
234
|
|
|
269
235
|
console.log();
|
|
270
236
|
console.error(`Bunoshfile not found: ${tasksFile}`);
|
|
271
|
-
console.log(customBunoshfile ?
|
|
237
|
+
console.log(customBunoshfile ?
|
|
272
238
|
`Run \`bunosh init\` in the directory or specify a valid --bunoshfile path` :
|
|
273
239
|
"Run `bunosh init` to create a new Bunoshfile here")
|
|
274
240
|
console.log();
|
|
@@ -277,9 +243,8 @@ async function main() {
|
|
|
277
243
|
|
|
278
244
|
await import('./index.js');
|
|
279
245
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
await bunosh(tasks, sources);
|
|
246
|
+
const { tasks, sources } = await loadBunoshfiles(tasksFile);
|
|
247
|
+
await bunosh(tasks, sources);
|
|
283
248
|
}
|
|
284
249
|
|
|
285
250
|
main().catch((error) => {
|
|
@@ -287,27 +252,22 @@ main().catch((error) => {
|
|
|
287
252
|
process.exit(1);
|
|
288
253
|
});
|
|
289
254
|
|
|
290
|
-
// Handle unhandled promise rejections
|
|
291
255
|
process.on('unhandledRejection', (reason, promise) => {
|
|
292
256
|
if (!process.env.BUNOSH_COMMAND_STARTED) return;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
console.error(
|
|
257
|
+
|
|
258
|
+
if (reason instanceof Error) {
|
|
259
|
+
console.error('\n' + formatError(reason));
|
|
260
|
+
} else {
|
|
261
|
+
console.error('\n' + color.red.bold('Unhandled Promise Rejection:'));
|
|
262
|
+
console.error(reason);
|
|
298
263
|
}
|
|
299
264
|
process.exit(1);
|
|
300
265
|
});
|
|
301
266
|
|
|
302
|
-
// Handle uncaught exceptions
|
|
303
267
|
process.on('uncaughtException', (error) => {
|
|
304
268
|
if (!process.env.BUNOSH_COMMAND_STARTED) return;
|
|
305
|
-
|
|
306
|
-
console.error('\n
|
|
307
|
-
console.error(error.message);
|
|
308
|
-
if (error.stack) {
|
|
309
|
-
console.error(error.stack);
|
|
310
|
-
}
|
|
269
|
+
|
|
270
|
+
console.error('\n' + formatError(error));
|
|
311
271
|
process.exit(1);
|
|
312
272
|
});
|
|
313
273
|
|
|
@@ -327,7 +287,6 @@ process.on('exit', (code) => {
|
|
|
327
287
|
|
|
328
288
|
const commandArgs = process.argv.slice(2);
|
|
329
289
|
|
|
330
|
-
// Test environment detection
|
|
331
290
|
const isTestEnvironment = process.env.NODE_ENV === 'test' ||
|
|
332
291
|
(typeof jest !== 'undefined' && jest.isRunning) ||
|
|
333
292
|
(process.env.VITEST_WORKER_ID !== undefined) ||
|
|
@@ -349,90 +308,9 @@ process.on('exit', (code) => {
|
|
|
349
308
|
const success = finalExitCode === 0;
|
|
350
309
|
|
|
351
310
|
if (globalThis._bunoshExitHandlerCalled) {
|
|
352
|
-
console.log('\n[DEBUG] Exit handler already called, skipping duplicate');
|
|
353
311
|
return;
|
|
354
312
|
}
|
|
355
313
|
globalThis._bunoshExitHandlerCalled = true;
|
|
356
314
|
|
|
357
315
|
console.log(`\n🍲 ${success ? '' : 'FAIL '}Exit Code: ${finalExitCode} | Tasks: ${tasksExecuted.length}${tasksFailed ? ` | Failed: ${tasksFailed}` : ''}${tasksWarning ? ` | Warnings: ${tasksWarning}` : ''} | Time: ${totalTime}ms`);
|
|
358
316
|
});
|
|
359
|
-
|
|
360
|
-
function handleBunoshfileError(error, filePath) {
|
|
361
|
-
// Don't show banner for errors - it interferes with error visibility
|
|
362
|
-
|
|
363
|
-
// Check for Babel parser syntax errors
|
|
364
|
-
if (error.code === 'BABEL_PARSER_SYNTAX_ERROR' ||
|
|
365
|
-
(error.reasonCode && error.loc) ||
|
|
366
|
-
error.constructor.name === 'SyntaxError') {
|
|
367
|
-
|
|
368
|
-
console.error(`❌ Syntax Error in ${path.basename(filePath)}:`);
|
|
369
|
-
console.log();
|
|
370
|
-
|
|
371
|
-
if (error.loc) {
|
|
372
|
-
console.error(` Line ${error.loc.line}, Column ${error.loc.column}:`);
|
|
373
|
-
|
|
374
|
-
// Provide specific error messages based on reasonCode
|
|
375
|
-
if (error.reasonCode === 'VarRedeclaration') {
|
|
376
|
-
console.error(` Variable redeclaration - '${error.message}' is already declared`);
|
|
377
|
-
} else if (error.reasonCode && error.reasonCode.includes('Unexpected')) {
|
|
378
|
-
console.error(` ${error.reasonCode}: ${error.message || 'Unexpected token'}`);
|
|
379
|
-
} else {
|
|
380
|
-
console.error(` ${error.message || error.reasonCode || 'Invalid syntax'}`);
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
console.error(` ${error.message || 'Invalid JavaScript syntax'}`);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
console.log();
|
|
387
|
-
console.log('💡 Common issues:');
|
|
388
|
-
console.log(' • Missing semicolons or commas');
|
|
389
|
-
console.log(' • Unclosed brackets, parentheses, or quotes');
|
|
390
|
-
console.log(' • Invalid variable declarations');
|
|
391
|
-
console.log(' • Mixing import/export with require/module.exports');
|
|
392
|
-
console.log();
|
|
393
|
-
console.log(`📝 Edit your Bunoshfile: ${color.blue('bunosh edit')}`);
|
|
394
|
-
console.log(`🔧 Validate syntax: ${color.blue(`bun --check ${path.basename(filePath)}`)}`);
|
|
395
|
-
|
|
396
|
-
} else if (error.message && error.message.includes('SyntaxError')) {
|
|
397
|
-
console.error(`❌ JavaScript Syntax Error in ${path.basename(filePath)}:`);
|
|
398
|
-
console.log();
|
|
399
|
-
console.error(` ${error.message}`);
|
|
400
|
-
console.log();
|
|
401
|
-
console.log(`💡 Try running: ${color.blue('bun --check Bunoshfile.js')}`);
|
|
402
|
-
console.log(`📝 Edit your Bunoshfile: ${color.blue('bunosh edit')}`);
|
|
403
|
-
|
|
404
|
-
} else if (error.code === 'MODULE_NOT_FOUND' ||
|
|
405
|
-
error.message?.includes('Cannot resolve') ||
|
|
406
|
-
error.message?.includes('Could not resolve')) {
|
|
407
|
-
console.error(`❌ Module Import Error in ${path.basename(filePath)}:`);
|
|
408
|
-
console.log();
|
|
409
|
-
console.error(` ${error.message}`);
|
|
410
|
-
console.log();
|
|
411
|
-
console.log('💡 Common solutions:');
|
|
412
|
-
console.log(` • Run: ${color.blue('bun install')}`);
|
|
413
|
-
console.log(' • Check import paths are correct');
|
|
414
|
-
console.log(' • Ensure dependencies are listed in package.json');
|
|
415
|
-
|
|
416
|
-
} else {
|
|
417
|
-
console.error(`❌ Error loading ${path.basename(filePath)}:`);
|
|
418
|
-
console.log();
|
|
419
|
-
console.error(` ${error.message || error.toString()}`);
|
|
420
|
-
|
|
421
|
-
// Add stack trace for debugging if available
|
|
422
|
-
if (process.env.BUNOSH_DEBUG) {
|
|
423
|
-
console.log();
|
|
424
|
-
console.log('🐛 Debug stack trace:');
|
|
425
|
-
console.log(error.stack || 'No stack trace available');
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
console.log();
|
|
429
|
-
console.log('💡 Try:');
|
|
430
|
-
console.log(` • Check the file exists: ${color.blue(`ls -la ${path.basename(filePath)}`)}`);
|
|
431
|
-
console.log(` • Validate syntax: ${color.blue(`bun --check ${path.basename(filePath)}`)}`);
|
|
432
|
-
console.log(` • Edit the file: ${color.blue('bunosh edit')}`);
|
|
433
|
-
console.log(` • Run with debug: ${color.blue('BUNOSH_DEBUG=1 bunosh')}`);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
console.log();
|
|
437
|
-
process.exit(1);
|
|
438
|
-
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { codeFrameColumns } from "@babel/code-frame";
|
|
2
|
+
import { readFileSync, existsSync } from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import color from "chalk";
|
|
5
|
+
|
|
6
|
+
function parseErrorLocation(error) {
|
|
7
|
+
if (!error.stack) return null;
|
|
8
|
+
|
|
9
|
+
const lines = error.stack.split('\n');
|
|
10
|
+
for (const line of lines) {
|
|
11
|
+
const match = line.match(/at .+? \((.+?):(\d+):(\d+)\)/) ||
|
|
12
|
+
line.match(/at (.+?):(\d+):(\d+)/);
|
|
13
|
+
if (!match) continue;
|
|
14
|
+
|
|
15
|
+
const file = match[1];
|
|
16
|
+
if (file.includes('node_modules/') || file.startsWith('node:')) continue;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
file,
|
|
20
|
+
line: parseInt(match[2], 10),
|
|
21
|
+
column: parseInt(match[3], 10),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function cleanStack(error) {
|
|
29
|
+
if (!error.stack) return '';
|
|
30
|
+
|
|
31
|
+
return error.stack
|
|
32
|
+
.split('\n')
|
|
33
|
+
.filter(line => {
|
|
34
|
+
if (!line.trim().startsWith('at ')) return false;
|
|
35
|
+
if (line.includes('node_modules/')) return false;
|
|
36
|
+
if (line.includes('node:')) return false;
|
|
37
|
+
return true;
|
|
38
|
+
})
|
|
39
|
+
.join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatError(error) {
|
|
43
|
+
const loc = parseErrorLocation(error);
|
|
44
|
+
const parts = [];
|
|
45
|
+
|
|
46
|
+
if (loc) {
|
|
47
|
+
const displayFile = path.relative(process.cwd(), loc.file) || loc.file;
|
|
48
|
+
parts.push(color.red.bold('Error') + ` in ${color.bold(displayFile + ':' + loc.line)}`);
|
|
49
|
+
} else {
|
|
50
|
+
parts.push(color.red.bold('Error'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
parts.push(` ${error.message}`);
|
|
54
|
+
|
|
55
|
+
if (loc) {
|
|
56
|
+
const absFile = path.isAbsolute(loc.file) ? loc.file : path.resolve(process.cwd(), loc.file);
|
|
57
|
+
if (existsSync(absFile)) {
|
|
58
|
+
try {
|
|
59
|
+
const source = readFileSync(absFile, 'utf-8');
|
|
60
|
+
const frame = codeFrameColumns(source, {
|
|
61
|
+
start: { line: loc.line, column: loc.column },
|
|
62
|
+
}, {
|
|
63
|
+
highlightCode: true,
|
|
64
|
+
linesAbove: 2,
|
|
65
|
+
linesBelow: 1,
|
|
66
|
+
});
|
|
67
|
+
parts.push('');
|
|
68
|
+
parts.push(frame);
|
|
69
|
+
} catch {}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const stack = cleanStack(error);
|
|
74
|
+
if (stack) {
|
|
75
|
+
parts.push('');
|
|
76
|
+
parts.push(stack);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return parts.join('\n');
|
|
80
|
+
}
|