/
parse-error.js
176 lines (154 loc) 路 6.5 KB
/
parse-error.js
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
// @flow
import { Position } from "./util/location";
import type { NodeBase } from "./types";
import {
instantiate,
type ParseErrorCode,
ParseErrorCodes,
type ParseErrorCredentials,
} from "./parse-error/credentials";
// 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 publically 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?: string;
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> = ({
loc: Position,
details: ErrorDetails,
}) => ParseError<ErrorDetails>;
function toParseErrorConstructor<ErrorDetails: Object>({
toMessage,
...properties
}: ParseErrorCredentials<ErrorDetails>): ParseErrorConstructor<ErrorDetails> {
return ({ loc, details }: { loc: Position, details: ErrorDetails }) =>
instantiate<ParseError<ErrorDetails>>(
SyntaxError,
{ ...properties, loc },
{
details: { value: details, enumerable: false },
message: {
get: ({ details, loc }) =>
`${toMessage(details)} (${loc.line}:${loc.column})`,
set: (self, value) =>
Object.defineProperty(self, "message", { value }),
},
pos: "loc.index",
missingPlugin: "missingPlugin" in details && "details.missingPlugin",
},
);
}
// This part is tricky. You'll probably notice from the name of this function
// that it is supposed to return `ParseErrorCredentials`, but instead these.
// declarations seem to instead imply that they return
// `ParseErrorConstructor<ErrorDetails>` instead. This is because in Flow we
// can't easily extract parameter types (either from functions, like with
// Typescript's Parameters<f> utility type, or from generic types either). As
// such, this function does double duty: packaging up the credentials during
// its actual runtime operation, but pretending to return the
// `ParseErrorConstructor<ErrorDetails>` that we won't actually have until later
// to the type system, avoiding the need to do so with $ObjMap (which doesn't
// work) in `ParseErrorEnum`. This hack won't be necessary when we switch to
// Typescript.
declare function toParseErrorCredentials<T: string>(
T,
?{ code?: ParseErrorCode, reasonCode?: string } | boolean,
): ParseErrorConstructor<{||}>;
// ESLint seems to erroneously think that Flow's overloading syntax is an
// accidental redeclaration of the function:
// https://github.com/babel/eslint-plugin-babel/issues/162
// eslint-disable-next-line no-redeclare
declare function toParseErrorCredentials<ErrorDetails>(
(ErrorDetails) => string,
?{ code?: ParseErrorCode, reasonCode?: string } | boolean,
): ParseErrorConstructor<ErrorDetails>;
// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
export function toParseErrorCredentials(toMessageOrMessage, credentials) {
return {
toMessage:
typeof toMessageOrMessage === "string"
? () => toMessageOrMessage
: toMessageOrMessage,
...credentials,
};
}
// This is the templated form.
declare function ParseErrorEnum(string[]): typeof ParseErrorEnum;
// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
declare function ParseErrorEnum<T>(
toParseErrorCredentials: (typeof toParseErrorCredentials) => T,
syntaxPlugin?: string,
): T;
// You call `ParseErrorEnum` with a mapping from `ReasonCode`'s to either error
// messages, or `toMessage` functions that define additional necessary `details`
// needed by the `ParseError`:
//
// ParseErrorEnum`optionalSyntaxPlugin` (_ => ({
// ErrorWithStaticMessage: _("message"),
// ErrorWithDynamicMessage: _<{ type: string }>(({ type }) => `${type}`),
// });
//
// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
export function ParseErrorEnum(argument, 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 toParseErrorCredentialsMap =>
ParseErrorEnum(toParseErrorCredentialsMap, argument[0]);
}
const partialCredentials = argument(toParseErrorCredentials);
const ParseErrorConstructors = {};
for (const reasonCode of Object.keys(partialCredentials)) {
ParseErrorConstructors[reasonCode] = toParseErrorConstructor({
code: ParseErrorCodes.SyntaxError,
reasonCode,
...(syntaxPlugin ? { syntaxPlugin } : {}),
...partialCredentials[reasonCode],
});
}
return ParseErrorConstructors;
}
export type RaiseProperties<ErrorDetails> = {|
...ErrorDetails,
at: Position | NodeBase,
|};
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";