|
1 | 1 | import * as assert from 'node:assert/strict'; |
2 | 2 | import { describe, it } from 'node:test'; |
3 | | -import { renderErrorMarkdown } from '../../../dist/core/errors/dev/utils.js'; |
| 3 | +import { collectErrorMetadata, renderErrorMarkdown } from '../../../dist/core/errors/dev/utils.js'; |
4 | 4 |
|
5 | 5 | describe('renderErrorMarkdown', () => { |
6 | 6 | describe('html target', () => { |
@@ -222,3 +222,70 @@ describe('renderErrorMarkdown', () => { |
222 | 222 | }); |
223 | 223 | }); |
224 | 224 | }); |
| 225 | + |
| 226 | +describe('collectErrorMetadata', () => { |
| 227 | + it('uses sub-error properties when the parent error has an errors array', () => { |
| 228 | + // Simulate how rolldown/Vite wraps a plugin error: the parent error has |
| 229 | + // its own stack but no loc/plugin, while the sub-error carries the real info. |
| 230 | + const subError = new Error('Something went wrong in component.astro'); |
| 231 | + subError.stack = `Error: Something went wrong in component.astro |
| 232 | + at renderComponent (file:///project/src/components/Foo.astro:10:5)`; |
| 233 | + |
| 234 | + const parentError = new Error('Build failed with 1 error'); |
| 235 | + parentError.stack = `Error: Build failed with 1 error |
| 236 | + at buildEnvironment (file:///node_modules/vite/dist/node/chunks/node.js:33011:66)`; |
| 237 | + // @ts-ignore - adding errors array like rolldown does |
| 238 | + parentError.errors = [subError]; |
| 239 | + |
| 240 | + const result = collectErrorMetadata(parentError); |
| 241 | + |
| 242 | + // The sub-error's own stack should be used, not the parent's |
| 243 | + assert.ok(result.stack?.includes('renderComponent')); |
| 244 | + assert.ok(!result.stack?.includes('buildEnvironment')); |
| 245 | + }); |
| 246 | + |
| 247 | + it('preserves sub-error loc when parent error has no loc', () => { |
| 248 | + const subError = new Error('Parse error'); |
| 249 | + // @ts-ignore |
| 250 | + subError.loc = { file: '/project/src/pages/test.mdx', line: 10, column: 5 }; |
| 251 | + // @ts-ignore |
| 252 | + subError.plugin = 'astro:mdx'; |
| 253 | + subError.stack = `Error: Parse error |
| 254 | + at transform (file:///project/node_modules/@astrojs/mdx/dist/index.js:42:7)`; |
| 255 | + |
| 256 | + const parentError = new Error('Build failed'); |
| 257 | + parentError.stack = `Error: Build failed |
| 258 | + at buildEnvironment (file:///node_modules/vite/dist/node.js:100:20)`; |
| 259 | + // @ts-ignore |
| 260 | + parentError.errors = [subError]; |
| 261 | + |
| 262 | + const result = collectErrorMetadata(parentError); |
| 263 | + |
| 264 | + assert.equal(result.loc?.file, '/project/src/pages/test.mdx'); |
| 265 | + assert.equal(result.loc?.line, 10); |
| 266 | + assert.equal(result.loc?.column, 5); |
| 267 | + assert.equal(result.plugin, 'astro:mdx'); |
| 268 | + }); |
| 269 | + |
| 270 | + it('does not generate misleading hints from parent error message', () => { |
| 271 | + // The sub-error message has no browser API references, but the parent's |
| 272 | + // stack/message might mention "window" or "document" incidentally. |
| 273 | + const subError = new Error('Could not parse expression with oxc'); |
| 274 | + subError.stack = `Error: Could not parse expression with oxc |
| 275 | + at transform (file:///project/node_modules/@astrojs/mdx/dist/index.js:42:7)`; |
| 276 | + |
| 277 | + const parentError = new Error('Build failed - check document for details'); |
| 278 | + parentError.stack = `Error: Build failed |
| 279 | + at build (file:///node_modules/vite/dist/node.js:100:20)`; |
| 280 | + // @ts-ignore |
| 281 | + parentError.errors = [subError]; |
| 282 | + |
| 283 | + const result = collectErrorMetadata(parentError); |
| 284 | + |
| 285 | + // Should not get a "Browser APIs are not available" hint from the parent message |
| 286 | + assert.ok( |
| 287 | + !result.hint?.includes('Browser APIs'), |
| 288 | + 'Should not generate browser API hint from parent error', |
| 289 | + ); |
| 290 | + }); |
| 291 | +}); |
0 commit comments