forked from babel/babel
/
parse-error.ts
221 lines (201 loc) 路 7.37 KB
/
parse-error.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
import { Position } from "./util/location";
import type { NodeBase } from "./types";
import {
instantiate,
ParseErrorCode,
type ParseErrorCredentials,
type ToMessage,
type SyntaxPlugin,
} from "./parse-error/credentials";
import type { Undone } from "../src/parser/node";
// Babel uses "normal" SyntaxErrors for it's errors, but adds some extra
// functionality. This functionality is defined in the
// `ParseErrorSpecification` interface below. We may choose to change to someday
// give our errors their own full-blown class, but until then this allow us to
// keep all the desirable properties of SyntaxErrors (like their name in stack
// traces, etc.), and also allows us to punt on any publicly facing
// class-hierarchy decisions until Babel 8.
interface ParseErrorSpecification<ErrorDetails> {
// Look, these *could* be readonly, but then Flow complains when we initially
// set them. We could do a whole dance and make a special interface that's not
// readonly for when we create the error, then cast it to the readonly
// interface for public use, but the previous implementation didn't have them
// as readonly, so let's just not worry about it for now.
code: ParseErrorCode;
reasonCode: string;
syntaxPlugin?: SyntaxPlugin;
missingPlugin?: string | string[];
loc: Position;
details: ErrorDetails;
// We should consider removing this as it now just contains the same
// information as `loc.index`.
// pos: number;
}
export type ParseError<ErrorDetails> = SyntaxError &
ParseErrorSpecification<ErrorDetails>;
// By `ParseErrorConstructor`, we mean something like the new-less style
// `ErrorConstructor`[1], since `ParseError`'s are not themselves actually
// separate classes from `SyntaxError`'s.
//
// 1. https://github.com/microsoft/TypeScript/blob/v4.5.5/lib/lib.es5.d.ts#L1027
export type ParseErrorConstructor<ErrorDetails> = (a: {
loc: Position;
details: ErrorDetails;
}) => ParseError<ErrorDetails>;
function toParseErrorConstructor<ErrorDetails>({
toMessage,
...properties
}: ParseErrorCredentials<ErrorDetails>): ParseErrorConstructor<ErrorDetails> {
type ConstructorArgument = {
loc: Position;
details: ErrorDetails;
};
return function constructor({ loc, details }: ConstructorArgument) {
return instantiate(
SyntaxError,
{ ...properties, loc },
{
clone(
overrides: {
loc?: Position;
details?: ErrorDetails;
} = {},
) {
const loc = (overrides.loc || {}) as Partial<Position>;
return constructor({
loc: new Position(
"line" in loc ? loc.line : this.loc.line,
"column" in loc ? loc.column : this.loc.column,
"index" in loc ? loc.index : this.loc.index,
),
details: { ...this.details, ...overrides.details },
});
},
details: { value: details, enumerable: false },
message: {
get(this: ConstructorArgument): string {
return `${toMessage(this.details)} (${this.loc.line}:${
this.loc.column
})`;
},
set(value: string) {
Object.defineProperty(this, "message", { value });
},
},
pos: { reflect: "loc.index", enumerable: true },
missingPlugin: "missingPlugin" in details && {
reflect: "details.missingPlugin",
enumerable: true,
},
},
) as ParseError<ErrorDetails>;
};
}
type ParseErrorTemplate =
| string
| ToMessage<any>
| { message: string | ToMessage<any> };
type ParseErrorTemplates = { [reasonCode: string]: ParseErrorTemplate };
// This is the templated form of `ParseErrorEnum`.
//
// Note: We could factor out the return type calculation into something like
// `ParseErrorConstructor<T extends ParseErrorTemplates>`, and then we could
// reuse it in the non-templated form of `ParseErrorEnum`, but TypeScript
// doesn't seem to drill down that far when showing you the computed type of
// an object in an editor, so we'll leave it inlined for now.
export function ParseErrorEnum(a: TemplateStringsArray): <
T extends ParseErrorTemplates,
>(
parseErrorTemplates: T,
) => {
[K in keyof T]: ParseErrorConstructor<
T[K] extends { message: string | ToMessage<any> }
? T[K]["message"] extends ToMessage<any>
? Parameters<T[K]["message"]>[0]
: {}
: T[K] extends ToMessage<any>
? Parameters<T[K]>[0]
: {}
>;
};
export function ParseErrorEnum<T extends ParseErrorTemplates>(
parseErrorTemplates: T,
syntaxPlugin?: SyntaxPlugin,
): {
[K in keyof T]: ParseErrorConstructor<
T[K] extends { message: string | ToMessage<any> }
? T[K]["message"] extends ToMessage<any>
? Parameters<T[K]["message"]>[0]
: {}
: T[K] extends ToMessage<any>
? Parameters<T[K]>[0]
: {}
>;
};
// You call `ParseErrorEnum` with a mapping from `ReasonCode`'s to either:
//
// 1. a static error message,
// 2. `toMessage` functions that define additional necessary `details` needed by
// the `ParseError`, or
// 3. Objects that contain a `message` of one of the above and overridden `code`
// and/or `reasonCode`:
//
// ParseErrorEnum `optionalSyntaxPlugin` ({
// ErrorWithStaticMessage: "message",
// ErrorWithDynamicMessage: ({ type } : { type: string }) => `${type}`),
// ErrorWithOverriddenCodeAndOrReasonCode: {
// message: ({ type }: { type: string }) => `${type}`),
// code: ParseErrorCode.SourceTypeModuleError,
// ...(BABEL_8_BREAKING ? { } : { reasonCode: "CustomErrorReasonCode" })
// }
// });
//
export function ParseErrorEnum(
argument: TemplateStringsArray | ParseErrorTemplates,
syntaxPlugin?: SyntaxPlugin,
) {
// If the first parameter is an array, that means we were called with a tagged
// template literal. Extract the syntaxPlugin from this, and call again in
// the "normalized" form.
if (Array.isArray(argument)) {
return (parseErrorTemplates: ParseErrorTemplates) =>
ParseErrorEnum(parseErrorTemplates, argument[0]);
}
const ParseErrorConstructors = {} as Record<
string,
ParseErrorConstructor<unknown>
>;
for (const reasonCode of Object.keys(argument)) {
const template = (argument as ParseErrorTemplates)[reasonCode];
const { message, ...rest } =
typeof template === "string"
? { message: () => template }
: typeof template === "function"
? { message: template }
: template;
const toMessage = typeof message === "string" ? () => message : message;
ParseErrorConstructors[reasonCode] = toParseErrorConstructor({
code: ParseErrorCode.SyntaxError,
reasonCode,
toMessage,
...(syntaxPlugin ? { syntaxPlugin } : {}),
...rest,
});
}
return ParseErrorConstructors;
}
export type RaiseProperties<ErrorDetails> = {
at: Position | Undone<NodeBase>;
} & ErrorDetails;
import ModuleErrors from "./parse-error/module-errors";
import StandardErrors from "./parse-error/standard-errors";
import StrictModeErrors from "./parse-error/strict-mode-errors";
import PipelineOperatorErrors from "./parse-error/pipeline-operator-errors";
export const Errors = {
...ParseErrorEnum(ModuleErrors),
...ParseErrorEnum(StandardErrors),
...ParseErrorEnum(StrictModeErrors),
...ParseErrorEnum`pipelineOperator`(PipelineOperatorErrors),
};
export type { LValAncestor } from "./parse-error/standard-errors";
export * from "./parse-error/credentials";