Skip to content

Commit 838bb63

Browse files
committed
feat: add better errors with miette from oxc, rich diagnostics
1 parent fa5ee55 commit 838bb63

File tree

8 files changed

+583
-54
lines changed

8 files changed

+583
-54
lines changed

packages/facetpack-native/README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,52 @@ const result = parseSync('App.tsx', code, {
4141

4242
console.log(result.program)
4343
console.log(result.errors)
44+
console.log(result.diagnostics)
4445
```
4546

47+
### Rich Diagnostics
48+
49+
Parse errors include detailed diagnostics with contextual help messages, similar to Rust compiler errors:
50+
51+
```ts
52+
const result = parseSync('config.ts', `const obj = {
53+
name: 'test',
54+
items: [
55+
{ id: 1
56+
]
57+
}`)
58+
59+
result.diagnostics.forEach(d => console.log(d.formatted))
60+
```
61+
62+
Output:
63+
```
64+
× Expected `,` or `}` but found `]`
65+
╭─[config.ts:5:3]
66+
1 │ const obj = {
67+
2 │ name: 'test',
68+
3 │ items: [
69+
4 │ { id: 1
70+
· ┬
71+
· ╰── Opened here
72+
5 │ ]
73+
· ┬
74+
· ╰── `,` or `}` expected
75+
6 │ }
76+
╰────
77+
help: Check for missing punctuation or unclosed brackets
78+
```
79+
80+
Each diagnostic includes:
81+
- `severity` - Error, Warning, Info, or Hint
82+
- `code` - Error code (e.g., E0001)
83+
- `message` - Error description
84+
- `filename`, `line`, `column` - Location info
85+
- `snippet` - Relevant source code line
86+
- `help` - Contextual suggestion to fix the error
87+
- `suggestion` - Specific fix recommendation
88+
- `formatted` - Pre-formatted colored output with visual pointers
89+
4690
### Resolve
4791

4892
```ts

packages/facetpack-native/index.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,29 @@ export declare function analyzeBatchSync(modules: Array<ModuleInput>): Array<Mod
1111

1212
export declare function analyzeSync(filename: string, sourceText: string): ModuleAnalysis
1313

14+
export interface Diagnostic {
15+
severity: DiagnosticSeverity
16+
code?: string
17+
message: string
18+
filename: string
19+
line: number
20+
column: number
21+
endLine?: number
22+
endColumn?: number
23+
snippet?: string
24+
label?: string
25+
help?: string
26+
suggestion?: string
27+
formatted: string
28+
}
29+
30+
export declare const enum DiagnosticSeverity {
31+
Error = 'Error',
32+
Warning = 'Warning',
33+
Info = 'Info',
34+
Hint = 'Hint'
35+
}
36+
1437
export interface ExportInfo {
1538
name: string
1639
isDefault: boolean
@@ -64,6 +87,7 @@ export interface ParseOptions {
6487
export interface ParseResult {
6588
program: string
6689
errors: Array<string>
90+
diagnostics: Array<Diagnostic>
6791
panicked: boolean
6892
}
6993

@@ -115,6 +139,7 @@ export interface TransformResult {
115139
code: string
116140
map?: string
117141
errors: Array<string>
142+
diagnostics: Array<Diagnostic>
118143
}
119144

120145
export declare function transformSync(filename: string, sourceText: string, options?: TransformOptions | undefined | null): TransformResult

packages/facetpack-native/index.js

Lines changed: 53 additions & 52 deletions
Large diffs are not rendered by default.

packages/facetpack-native/src/cqrs/command/minify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ mod tests {
149149
"#;
150150
let options = MinifyOptions {
151151
mangle: Some(false),
152-
compress: Some(false), // Disable compress to avoid dead code elimination
152+
compress: Some(false),
153153
..Default::default()
154154
};
155155
let command = MinifyCommand::new(code.to_string(), "test.js".to_string(), Some(options));

packages/facetpack-native/src/cqrs/command/transform.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ impl Command for TransformCommand {
131131
code: codegen_return.code,
132132
map,
133133
errors,
134+
diagnostics: vec![],
134135
})
135136
}
136137
}
@@ -294,4 +295,42 @@ mod tests {
294295

295296
assert!(result.errors.is_empty());
296297
}
298+
299+
#[test]
300+
fn test_error_transform_invalid_jsx_attribute() {
301+
let code = r#"const App = () => <View style= />;"#;
302+
let command = TransformCommand::new("Component.tsx".to_string(), code.to_string(), None);
303+
let result = command.execute();
304+
assert!(result.is_err());
305+
}
306+
307+
#[test]
308+
fn test_error_transform_unclosed_jsx_tag() {
309+
let code = r#"const App = () => <View><Text>Hello</View>;"#;
310+
let command = TransformCommand::new("App.tsx".to_string(), code.to_string(), None);
311+
let result = command.execute();
312+
assert!(result.is_err());
313+
}
314+
315+
#[test]
316+
fn test_error_transform_invalid_typescript_generic() {
317+
let code = "const fn = <T,>(x: T) => x; fn<>(5);";
318+
let command = TransformCommand::new("generic.ts".to_string(), code.to_string(), None);
319+
let _result = command.execute();
320+
}
321+
322+
#[test]
323+
fn test_error_transform_mixed_jsx_children() {
324+
let code = r#"const App = () => <View>{items.map(i => <Text key={i}>{i}</Text>)}</View>;"#;
325+
let command = TransformCommand::new("List.tsx".to_string(), code.to_string(), None);
326+
let result = command.execute();
327+
assert!(result.is_ok());
328+
}
329+
330+
#[test]
331+
fn test_error_transform_async_in_wrong_context() {
332+
let code = r#"const x = await fetch('/api');"#;
333+
let command = TransformCommand::new("api.ts".to_string(), code.to_string(), None);
334+
let _result = command.execute();
335+
}
297336
}

0 commit comments

Comments
 (0)