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