@openrewrite/rewrite 8.69.0-20251211-160325 → 8.69.0-20251212-112414
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/dist/cli/cli-utils.d.ts.map +1 -1
- package/dist/cli/cli-utils.js +110 -71
- package/dist/cli/cli-utils.js.map +1 -1
- package/dist/javascript/package-json-parser.d.ts +0 -5
- package/dist/javascript/package-json-parser.d.ts.map +1 -1
- package/dist/javascript/package-json-parser.js +2 -12
- package/dist/javascript/package-json-parser.js.map +1 -1
- package/dist/javascript/package-manager.d.ts +72 -1
- package/dist/javascript/package-manager.d.ts.map +1 -1
- package/dist/javascript/package-manager.js +173 -2
- package/dist/javascript/package-manager.js.map +1 -1
- package/dist/javascript/recipes/add-dependency.d.ts.map +1 -1
- package/dist/javascript/recipes/add-dependency.js +11 -8
- package/dist/javascript/recipes/add-dependency.js.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-dependency-version.js +11 -8
- package/dist/javascript/recipes/upgrade-dependency-version.js.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts.map +1 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js +11 -8
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js.map +1 -1
- package/dist/json/parser.d.ts.map +1 -1
- package/dist/json/parser.js +355 -123
- package/dist/json/parser.js.map +1 -1
- package/dist/json/tree.d.ts +5 -1
- package/dist/json/tree.d.ts.map +1 -1
- package/dist/json/tree.js +157 -7
- package/dist/json/tree.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +5 -1
- package/dist/print.js.map +1 -1
- package/dist/rpc/request/get-languages.d.ts.map +1 -1
- package/dist/rpc/request/get-languages.js +1 -0
- package/dist/rpc/request/get-languages.js.map +1 -1
- package/dist/rpc/server.d.ts +1 -0
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +1 -0
- package/dist/rpc/server.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/yaml/index.d.ts +6 -0
- package/dist/yaml/index.d.ts.map +1 -0
- package/dist/yaml/index.js +37 -0
- package/dist/yaml/index.js.map +1 -0
- package/dist/yaml/parser.d.ts +6 -0
- package/dist/yaml/parser.d.ts.map +1 -0
- package/dist/yaml/parser.js +803 -0
- package/dist/yaml/parser.js.map +1 -0
- package/dist/yaml/print.d.ts +2 -0
- package/dist/yaml/print.d.ts.map +1 -0
- package/dist/yaml/print.js +234 -0
- package/dist/yaml/print.js.map +1 -0
- package/dist/yaml/rpc.d.ts +2 -0
- package/dist/yaml/rpc.d.ts.map +1 -0
- package/dist/yaml/rpc.js +264 -0
- package/dist/yaml/rpc.js.map +1 -0
- package/dist/yaml/tree.d.ts +188 -0
- package/dist/yaml/tree.d.ts.map +1 -0
- package/dist/yaml/tree.js +117 -0
- package/dist/yaml/tree.js.map +1 -0
- package/dist/yaml/visitor.d.ts +19 -0
- package/dist/yaml/visitor.d.ts.map +1 -0
- package/dist/yaml/visitor.js +170 -0
- package/dist/yaml/visitor.js.map +1 -0
- package/package.json +6 -1
- package/src/cli/cli-utils.ts +112 -35
- package/src/javascript/package-json-parser.ts +2 -12
- package/src/javascript/package-manager.ts +179 -3
- package/src/javascript/recipes/add-dependency.ts +16 -10
- package/src/javascript/recipes/upgrade-dependency-version.ts +16 -10
- package/src/javascript/recipes/upgrade-transitive-dependency-version.ts +15 -9
- package/src/json/parser.ts +443 -146
- package/src/json/tree.ts +159 -7
- package/src/print.ts +6 -2
- package/src/rpc/request/get-languages.ts +1 -0
- package/src/rpc/server.ts +1 -0
- package/src/yaml/index.ts +21 -0
- package/src/yaml/parser.ts +850 -0
- package/src/yaml/print.ts +212 -0
- package/src/yaml/rpc.ts +248 -0
- package/src/yaml/tree.ts +281 -0
- package/src/yaml/visitor.ts +146 -0
package/src/json/parser.ts
CHANGED
|
@@ -14,11 +14,55 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import {emptyMarkers, markers, MarkersKind, ParseExceptionResult} from "../markers";
|
|
17
|
-
import {Parser, ParserInput, parserInputRead
|
|
17
|
+
import {Parser, ParserInput, parserInputRead} from "../parser";
|
|
18
18
|
import {randomId} from "../uuid";
|
|
19
19
|
import {SourceFile} from "../tree";
|
|
20
20
|
import {emptySpace, Json, space} from "./tree";
|
|
21
21
|
import {ParseError, ParseErrorKind} from "../parse-error";
|
|
22
|
+
import {createScanner} from "jsonc-parser";
|
|
23
|
+
|
|
24
|
+
// Define token types locally to avoid const enum issues with isolatedModules
|
|
25
|
+
const Token = {
|
|
26
|
+
OpenBraceToken: 1,
|
|
27
|
+
CloseBraceToken: 2,
|
|
28
|
+
OpenBracketToken: 3,
|
|
29
|
+
CloseBracketToken: 4,
|
|
30
|
+
CommaToken: 5,
|
|
31
|
+
ColonToken: 6,
|
|
32
|
+
NullKeyword: 7,
|
|
33
|
+
TrueKeyword: 8,
|
|
34
|
+
FalseKeyword: 9,
|
|
35
|
+
StringLiteral: 10,
|
|
36
|
+
NumericLiteral: 11,
|
|
37
|
+
LineCommentTrivia: 12,
|
|
38
|
+
BlockCommentTrivia: 13,
|
|
39
|
+
LineBreakTrivia: 14,
|
|
40
|
+
Trivia: 15,
|
|
41
|
+
Unknown: 16,
|
|
42
|
+
EOF: 17
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
type TokenType = typeof Token[keyof typeof Token];
|
|
46
|
+
|
|
47
|
+
const TokenNames: Record<number, string> = {
|
|
48
|
+
[Token.OpenBraceToken]: 'OpenBraceToken',
|
|
49
|
+
[Token.CloseBraceToken]: 'CloseBraceToken',
|
|
50
|
+
[Token.OpenBracketToken]: 'OpenBracketToken',
|
|
51
|
+
[Token.CloseBracketToken]: 'CloseBracketToken',
|
|
52
|
+
[Token.CommaToken]: 'CommaToken',
|
|
53
|
+
[Token.ColonToken]: 'ColonToken',
|
|
54
|
+
[Token.NullKeyword]: 'NullKeyword',
|
|
55
|
+
[Token.TrueKeyword]: 'TrueKeyword',
|
|
56
|
+
[Token.FalseKeyword]: 'FalseKeyword',
|
|
57
|
+
[Token.StringLiteral]: 'StringLiteral',
|
|
58
|
+
[Token.NumericLiteral]: 'NumericLiteral',
|
|
59
|
+
[Token.LineCommentTrivia]: 'LineCommentTrivia',
|
|
60
|
+
[Token.BlockCommentTrivia]: 'BlockCommentTrivia',
|
|
61
|
+
[Token.LineBreakTrivia]: 'LineBreakTrivia',
|
|
62
|
+
[Token.Trivia]: 'Trivia',
|
|
63
|
+
[Token.Unknown]: 'Unknown',
|
|
64
|
+
[Token.EOF]: 'EOF'
|
|
65
|
+
};
|
|
22
66
|
|
|
23
67
|
export class JsonParser extends Parser {
|
|
24
68
|
|
|
@@ -26,11 +70,11 @@ export class JsonParser extends Parser {
|
|
|
26
70
|
for (const sourcePath of sourcePaths) {
|
|
27
71
|
try {
|
|
28
72
|
yield {
|
|
29
|
-
...new
|
|
73
|
+
...new JsoncParserReader(parserInputRead(sourcePath)).parse(),
|
|
30
74
|
sourcePath: this.relativePath(sourcePath)
|
|
31
75
|
};
|
|
32
76
|
} catch (e: any) {
|
|
33
|
-
// Return a ParseError for files that can't be parsed
|
|
77
|
+
// Return a ParseError for files that can't be parsed
|
|
34
78
|
const text = parserInputRead(sourcePath);
|
|
35
79
|
const parseError: ParseError = {
|
|
36
80
|
kind: ParseErrorKind,
|
|
@@ -51,177 +95,393 @@ export class JsonParser extends Parser {
|
|
|
51
95
|
}
|
|
52
96
|
}
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Parser that uses jsonc-parser's scanner for tokenization.
|
|
100
|
+
* This is significantly faster than our custom character-by-character parsing.
|
|
101
|
+
*/
|
|
102
|
+
class JsoncParserReader {
|
|
103
|
+
private readonly source: string;
|
|
104
|
+
private readonly scanner: ReturnType<typeof createScanner>;
|
|
105
|
+
// Use explicit number type to prevent TypeScript from narrowing after switch cases
|
|
106
|
+
// (TypeScript doesn't know that consumeTrivia() and advance() modify this.token)
|
|
107
|
+
private token: number = 0;
|
|
108
|
+
private tokenOffset: number = 0;
|
|
109
|
+
private tokenLength: number = 0;
|
|
110
|
+
private tokenValue: string = '';
|
|
111
|
+
|
|
112
|
+
constructor(source: string) {
|
|
113
|
+
this.source = source;
|
|
114
|
+
// ignoreTrivia = false to get whitespace and comments
|
|
115
|
+
this.scanner = createScanner(source, false);
|
|
116
|
+
this.advance();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private advance(): number {
|
|
120
|
+
this.token = this.scanner.scan();
|
|
121
|
+
this.tokenOffset = this.scanner.getTokenOffset();
|
|
122
|
+
this.tokenLength = this.scanner.getTokenLength();
|
|
123
|
+
this.tokenValue = this.scanner.getTokenValue();
|
|
124
|
+
return this.token;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get current token. This method helps TypeScript understand that the token
|
|
129
|
+
* may have changed after calling consumeTrivia() or advance().
|
|
130
|
+
*/
|
|
131
|
+
private currentToken(): number {
|
|
132
|
+
return this.token;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Consumes all trivia (whitespace and comments) and returns them as a single string.
|
|
137
|
+
*/
|
|
138
|
+
private consumeTrivia(): string {
|
|
139
|
+
let trivia = '';
|
|
140
|
+
while (
|
|
141
|
+
this.token === Token.Trivia ||
|
|
142
|
+
this.token === Token.LineBreakTrivia ||
|
|
143
|
+
this.token === Token.LineCommentTrivia ||
|
|
144
|
+
this.token === Token.BlockCommentTrivia
|
|
145
|
+
) {
|
|
146
|
+
trivia += this.source.slice(this.tokenOffset, this.tokenOffset + this.tokenLength);
|
|
147
|
+
this.advance();
|
|
148
|
+
}
|
|
149
|
+
return trivia;
|
|
57
150
|
}
|
|
58
151
|
|
|
59
|
-
private prefix() {
|
|
60
|
-
return space(this.
|
|
152
|
+
private prefix(): Json.Space {
|
|
153
|
+
return space(this.consumeTrivia());
|
|
61
154
|
}
|
|
62
155
|
|
|
63
156
|
parse(): Omit<Json.Document, "sourcePath"> {
|
|
157
|
+
const prefix = this.prefix();
|
|
158
|
+
|
|
159
|
+
// Handle empty document
|
|
160
|
+
if (this.token === Token.EOF) {
|
|
161
|
+
return {
|
|
162
|
+
kind: Json.Kind.Document,
|
|
163
|
+
id: randomId(),
|
|
164
|
+
prefix,
|
|
165
|
+
markers: emptyMarkers,
|
|
166
|
+
value: {
|
|
167
|
+
kind: Json.Kind.Literal,
|
|
168
|
+
id: randomId(),
|
|
169
|
+
prefix: emptySpace,
|
|
170
|
+
markers: emptyMarkers,
|
|
171
|
+
source: this.source,
|
|
172
|
+
value: ''
|
|
173
|
+
} satisfies Json.Literal as Json.Literal as Json.Value,
|
|
174
|
+
eof: emptySpace
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const value = this.parseValue() as Json.Value;
|
|
179
|
+
const eof = this.prefix();
|
|
180
|
+
|
|
64
181
|
return {
|
|
65
182
|
kind: Json.Kind.Document,
|
|
66
183
|
id: randomId(),
|
|
67
|
-
prefix
|
|
184
|
+
prefix,
|
|
68
185
|
markers: emptyMarkers,
|
|
69
|
-
value
|
|
70
|
-
eof
|
|
71
|
-
}
|
|
186
|
+
value,
|
|
187
|
+
eof
|
|
188
|
+
};
|
|
72
189
|
}
|
|
73
190
|
|
|
74
|
-
private
|
|
191
|
+
private parseValue(): Json {
|
|
192
|
+
const prefix = this.prefix();
|
|
75
193
|
const base = {
|
|
76
194
|
id: randomId(),
|
|
77
|
-
prefix
|
|
195
|
+
prefix,
|
|
78
196
|
markers: emptyMarkers
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
switch (this.token) {
|
|
200
|
+
case Token.OpenBraceToken:
|
|
201
|
+
return this.parseObject(base);
|
|
202
|
+
case Token.OpenBracketToken:
|
|
203
|
+
return this.parseArray(base);
|
|
204
|
+
case Token.StringLiteral:
|
|
205
|
+
return this.parseStringLiteral(base);
|
|
206
|
+
case Token.NumericLiteral:
|
|
207
|
+
return this.parseNumericLiteral(base);
|
|
208
|
+
case Token.TrueKeyword:
|
|
209
|
+
this.advance();
|
|
210
|
+
return {
|
|
211
|
+
kind: Json.Kind.Literal,
|
|
212
|
+
...base,
|
|
213
|
+
source: 'true',
|
|
214
|
+
value: true
|
|
215
|
+
} satisfies Json.Literal as Json.Literal;
|
|
216
|
+
case Token.FalseKeyword:
|
|
217
|
+
this.advance();
|
|
218
|
+
return {
|
|
219
|
+
kind: Json.Kind.Literal,
|
|
220
|
+
...base,
|
|
221
|
+
source: 'false',
|
|
222
|
+
value: false
|
|
223
|
+
} satisfies Json.Literal as Json.Literal;
|
|
224
|
+
case Token.NullKeyword:
|
|
225
|
+
this.advance();
|
|
226
|
+
return {
|
|
227
|
+
kind: Json.Kind.Literal,
|
|
228
|
+
...base,
|
|
229
|
+
source: 'null',
|
|
230
|
+
value: null
|
|
231
|
+
} satisfies Json.Literal as Json.Literal;
|
|
232
|
+
default:
|
|
233
|
+
throw new Error(`Unexpected token ${TokenNames[this.token] || this.token} at offset ${this.tokenOffset}`);
|
|
79
234
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private parseObject(base: { id: string; prefix: Json.Space; markers: typeof emptyMarkers }): Json.Object {
|
|
238
|
+
this.advance(); // consume '{'
|
|
239
|
+
|
|
240
|
+
const members: Json.RightPadded<Json.Member>[] = [];
|
|
241
|
+
|
|
242
|
+
// Check for empty object
|
|
243
|
+
const afterOpen = this.consumeTrivia();
|
|
244
|
+
if (this.token === Token.CloseBraceToken) {
|
|
245
|
+
this.advance(); // consume '}'
|
|
246
|
+
members.push({
|
|
247
|
+
kind: Json.Kind.RightPadded,
|
|
248
|
+
element: {
|
|
249
|
+
kind: Json.Kind.Empty,
|
|
250
|
+
id: randomId(),
|
|
251
|
+
prefix: emptySpace,
|
|
95
252
|
markers: emptyMarkers
|
|
96
|
-
} satisfies Json.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
253
|
+
} satisfies Json.Empty as Json.Empty as unknown as Json.Member,
|
|
254
|
+
after: space(afterOpen),
|
|
255
|
+
markers: emptyMarkers
|
|
256
|
+
} satisfies Json.RightPadded<Json.Member> as Json.RightPadded<Json.Member>);
|
|
257
|
+
} else {
|
|
258
|
+
// Parse members
|
|
259
|
+
// Put back the trivia by prepending it to the key's prefix
|
|
260
|
+
let pendingTrivia = afterOpen;
|
|
261
|
+
|
|
262
|
+
while (true) {
|
|
263
|
+
const member = this.parseMember(pendingTrivia);
|
|
264
|
+
pendingTrivia = '';
|
|
265
|
+
|
|
266
|
+
const afterMember = this.consumeTrivia();
|
|
267
|
+
|
|
268
|
+
if (this.token === Token.CommaToken) {
|
|
269
|
+
this.advance(); // consume ','
|
|
270
|
+
|
|
271
|
+
// Check for trailing comma
|
|
272
|
+
const afterComma = this.consumeTrivia();
|
|
273
|
+
if (this.currentToken() === Token.CloseBraceToken) {
|
|
274
|
+
// Trailing comma
|
|
275
|
+
members.push({
|
|
276
|
+
kind: Json.Kind.RightPadded,
|
|
277
|
+
element: member,
|
|
278
|
+
after: space(afterMember),
|
|
279
|
+
markers: emptyMarkers
|
|
280
|
+
} satisfies Json.RightPadded<Json.Member> as Json.RightPadded<Json.Member>);
|
|
281
|
+
|
|
282
|
+
this.advance(); // consume '}'
|
|
283
|
+
members.push({
|
|
284
|
+
kind: Json.Kind.RightPadded,
|
|
285
|
+
element: {
|
|
286
|
+
kind: Json.Kind.Empty,
|
|
287
|
+
id: randomId(),
|
|
288
|
+
prefix: emptySpace,
|
|
289
|
+
markers: emptyMarkers
|
|
290
|
+
} satisfies Json.Empty as Json.Empty as unknown as Json.Member,
|
|
291
|
+
after: space(afterComma),
|
|
292
|
+
markers: emptyMarkers
|
|
293
|
+
} satisfies Json.RightPadded<Json.Member> as Json.RightPadded<Json.Member>);
|
|
294
|
+
break;
|
|
295
|
+
} else {
|
|
296
|
+
// More members - save trivia for next member's prefix
|
|
297
|
+
members.push({
|
|
298
|
+
kind: Json.Kind.RightPadded,
|
|
299
|
+
element: member,
|
|
300
|
+
after: space(afterMember),
|
|
301
|
+
markers: emptyMarkers
|
|
302
|
+
} satisfies Json.RightPadded<Json.Member> as Json.RightPadded<Json.Member>);
|
|
303
|
+
pendingTrivia = afterComma;
|
|
105
304
|
}
|
|
106
|
-
|
|
305
|
+
} else if (this.token === Token.CloseBraceToken) {
|
|
306
|
+
this.advance(); // consume '}'
|
|
307
|
+
members.push({
|
|
107
308
|
kind: Json.Kind.RightPadded,
|
|
108
|
-
element,
|
|
109
|
-
after: space(
|
|
309
|
+
element: member,
|
|
310
|
+
after: space(afterMember),
|
|
110
311
|
markers: emptyMarkers
|
|
111
|
-
} satisfies Json.RightPadded<Json.
|
|
112
|
-
|
|
312
|
+
} satisfies Json.RightPadded<Json.Member> as Json.RightPadded<Json.Member>);
|
|
313
|
+
break;
|
|
314
|
+
} else {
|
|
315
|
+
throw new Error(`Expected ',' or '}' at offset ${this.tokenOffset}, found ${TokenNames[this.token] || this.token}`);
|
|
316
|
+
}
|
|
113
317
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
kind: Json.Kind.Object,
|
|
322
|
+
...base,
|
|
323
|
+
members
|
|
324
|
+
} satisfies Json.Object as Json.Object;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private parseArray(base: { id: string; prefix: Json.Space; markers: typeof emptyMarkers }): Json.Array {
|
|
328
|
+
this.advance(); // consume '['
|
|
329
|
+
|
|
330
|
+
const values: Json.RightPadded<Json.Value>[] = [];
|
|
331
|
+
|
|
332
|
+
// Check for empty array
|
|
333
|
+
const afterOpen = this.consumeTrivia();
|
|
334
|
+
if (this.token === Token.CloseBracketToken) {
|
|
335
|
+
this.advance(); // consume ']'
|
|
336
|
+
values.push({
|
|
337
|
+
kind: Json.Kind.RightPadded,
|
|
338
|
+
element: {
|
|
339
|
+
kind: Json.Kind.Empty,
|
|
340
|
+
id: randomId(),
|
|
341
|
+
prefix: emptySpace,
|
|
342
|
+
markers: emptyMarkers
|
|
343
|
+
} satisfies Json.Empty as Json.Empty,
|
|
344
|
+
after: space(afterOpen),
|
|
345
|
+
markers: emptyMarkers
|
|
346
|
+
} satisfies Json.RightPadded<Json.Value> as Json.RightPadded<Json.Value>);
|
|
347
|
+
} else {
|
|
348
|
+
// Parse values - need to handle the trivia we already consumed
|
|
349
|
+
// by putting it back as a "pending" prefix
|
|
350
|
+
let pendingTrivia = afterOpen;
|
|
351
|
+
|
|
352
|
+
while (true) {
|
|
353
|
+
// For first value, prepend the afterOpen trivia
|
|
354
|
+
const valuePrefix = pendingTrivia + this.consumeTrivia();
|
|
355
|
+
pendingTrivia = '';
|
|
356
|
+
|
|
357
|
+
const valueBase = {
|
|
358
|
+
id: randomId(),
|
|
359
|
+
prefix: space(valuePrefix),
|
|
136
360
|
markers: emptyMarkers
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
let value: Json.Value;
|
|
364
|
+
switch (this.token) {
|
|
365
|
+
case Token.OpenBraceToken:
|
|
366
|
+
value = this.parseObject(valueBase);
|
|
367
|
+
break;
|
|
368
|
+
case Token.OpenBracketToken:
|
|
369
|
+
value = this.parseArray(valueBase);
|
|
370
|
+
break;
|
|
371
|
+
case Token.StringLiteral:
|
|
372
|
+
value = this.parseStringLiteral(valueBase);
|
|
373
|
+
break;
|
|
374
|
+
case Token.NumericLiteral:
|
|
375
|
+
value = this.parseNumericLiteral(valueBase);
|
|
376
|
+
break;
|
|
377
|
+
case Token.TrueKeyword:
|
|
378
|
+
this.advance();
|
|
379
|
+
value = {
|
|
380
|
+
kind: Json.Kind.Literal,
|
|
381
|
+
...valueBase,
|
|
382
|
+
source: 'true',
|
|
383
|
+
value: true
|
|
384
|
+
} satisfies Json.Literal as Json.Literal;
|
|
385
|
+
break;
|
|
386
|
+
case Token.FalseKeyword:
|
|
387
|
+
this.advance();
|
|
388
|
+
value = {
|
|
389
|
+
kind: Json.Kind.Literal,
|
|
390
|
+
...valueBase,
|
|
391
|
+
source: 'false',
|
|
392
|
+
value: false
|
|
393
|
+
} satisfies Json.Literal as Json.Literal;
|
|
394
|
+
break;
|
|
395
|
+
case Token.NullKeyword:
|
|
396
|
+
this.advance();
|
|
397
|
+
value = {
|
|
398
|
+
kind: Json.Kind.Literal,
|
|
399
|
+
...valueBase,
|
|
400
|
+
source: 'null',
|
|
401
|
+
value: null
|
|
402
|
+
} satisfies Json.Literal as Json.Literal;
|
|
403
|
+
break;
|
|
404
|
+
default:
|
|
405
|
+
throw new Error(`Unexpected token ${TokenNames[this.token] || this.token} in array at offset ${this.tokenOffset}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const afterValue = this.consumeTrivia();
|
|
409
|
+
|
|
410
|
+
if (this.currentToken() === Token.CommaToken) {
|
|
411
|
+
this.advance(); // consume ','
|
|
412
|
+
|
|
413
|
+
// Check for trailing comma
|
|
414
|
+
const afterComma = this.consumeTrivia();
|
|
415
|
+
if (this.currentToken() === Token.CloseBracketToken) {
|
|
416
|
+
// Trailing comma
|
|
417
|
+
values.push({
|
|
418
|
+
kind: Json.Kind.RightPadded,
|
|
419
|
+
element: value,
|
|
420
|
+
after: space(afterValue),
|
|
421
|
+
markers: emptyMarkers
|
|
422
|
+
} satisfies Json.RightPadded<Json.Value> as Json.RightPadded<Json.Value>);
|
|
423
|
+
|
|
424
|
+
this.advance(); // consume ']'
|
|
425
|
+
values.push({
|
|
426
|
+
kind: Json.Kind.RightPadded,
|
|
427
|
+
element: {
|
|
428
|
+
kind: Json.Kind.Empty,
|
|
429
|
+
id: randomId(),
|
|
430
|
+
prefix: emptySpace,
|
|
431
|
+
markers: emptyMarkers
|
|
432
|
+
} satisfies Json.Empty as Json.Empty,
|
|
433
|
+
after: space(afterComma),
|
|
434
|
+
markers: emptyMarkers
|
|
435
|
+
} satisfies Json.RightPadded<Json.Value> as Json.RightPadded<Json.Value>);
|
|
436
|
+
break;
|
|
437
|
+
} else {
|
|
438
|
+
// More values
|
|
439
|
+
values.push({
|
|
440
|
+
kind: Json.Kind.RightPadded,
|
|
441
|
+
element: value,
|
|
442
|
+
after: space(afterValue),
|
|
443
|
+
markers: emptyMarkers
|
|
444
|
+
} satisfies Json.RightPadded<Json.Value> as Json.RightPadded<Json.Value>);
|
|
445
|
+
pendingTrivia = afterComma;
|
|
146
446
|
}
|
|
147
|
-
|
|
447
|
+
} else if (this.currentToken() === Token.CloseBracketToken) {
|
|
448
|
+
this.advance(); // consume ']'
|
|
449
|
+
values.push({
|
|
148
450
|
kind: Json.Kind.RightPadded,
|
|
149
|
-
element,
|
|
150
|
-
after: space(
|
|
451
|
+
element: value,
|
|
452
|
+
after: space(afterValue),
|
|
151
453
|
markers: emptyMarkers
|
|
152
|
-
} satisfies Json.RightPadded<Json.
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
this.cursor++; // skip '}'
|
|
156
|
-
return {
|
|
157
|
-
kind: Json.Kind.Object,
|
|
158
|
-
...base,
|
|
159
|
-
members
|
|
160
|
-
} satisfies Json.Object as Json.Object;
|
|
161
|
-
} else if (typeof parsed === "string") {
|
|
162
|
-
// Extract original source to preserve escape sequences
|
|
163
|
-
const sourceStart = this.cursor;
|
|
164
|
-
this.cursor++; // skip opening quote
|
|
165
|
-
while (this.cursor < this.source.length) {
|
|
166
|
-
const char = this.source[this.cursor];
|
|
167
|
-
if (char === '\\') {
|
|
168
|
-
this.cursor += 2; // skip escape sequence
|
|
169
|
-
} else if (char === '"') {
|
|
170
|
-
this.cursor++; // skip closing quote
|
|
454
|
+
} satisfies Json.RightPadded<Json.Value> as Json.RightPadded<Json.Value>);
|
|
171
455
|
break;
|
|
172
456
|
} else {
|
|
173
|
-
this.
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const source = this.source.slice(sourceStart, this.cursor);
|
|
177
|
-
return {
|
|
178
|
-
kind: Json.Kind.Literal,
|
|
179
|
-
...base,
|
|
180
|
-
source,
|
|
181
|
-
value: parsed
|
|
182
|
-
} satisfies Json.Literal as Json.Literal;
|
|
183
|
-
} else if (typeof parsed === "number") {
|
|
184
|
-
// Extract original source to preserve precision for large numbers
|
|
185
|
-
const sourceStart = this.cursor;
|
|
186
|
-
// Numbers can have optional sign, digits, decimal point, and exponent
|
|
187
|
-
while (this.cursor < this.source.length) {
|
|
188
|
-
const char = this.source[this.cursor];
|
|
189
|
-
if (/[\d.eE+\-]/.test(char)) {
|
|
190
|
-
this.cursor++;
|
|
191
|
-
} else {
|
|
192
|
-
break;
|
|
457
|
+
throw new Error(`Expected ',' or ']' at offset ${this.tokenOffset}, found ${TokenNames[this.currentToken()] || this.currentToken()}`);
|
|
193
458
|
}
|
|
194
459
|
}
|
|
195
|
-
const source = this.source.slice(sourceStart, this.cursor);
|
|
196
|
-
return {
|
|
197
|
-
kind: Json.Kind.Literal,
|
|
198
|
-
...base,
|
|
199
|
-
source,
|
|
200
|
-
value: parsed,
|
|
201
|
-
} satisfies Json.Literal as Json.Literal;
|
|
202
|
-
} else if (typeof parsed === "boolean") {
|
|
203
|
-
const source = parsed ? "true" : "false";
|
|
204
|
-
this.cursor += source.length;
|
|
205
|
-
return {
|
|
206
|
-
kind: Json.Kind.Literal,
|
|
207
|
-
...base,
|
|
208
|
-
source,
|
|
209
|
-
value: parsed,
|
|
210
|
-
} satisfies Json.Literal as Json.Literal;
|
|
211
|
-
} else if (parsed === null) {
|
|
212
|
-
this.cursor += 4; // "null".length
|
|
213
|
-
return {
|
|
214
|
-
kind: Json.Kind.Literal,
|
|
215
|
-
...base,
|
|
216
|
-
source: "null",
|
|
217
|
-
value: null,
|
|
218
|
-
} satisfies Json.Literal as Json.Literal;
|
|
219
|
-
} else {
|
|
220
|
-
throw new Error(`Unsupported JSON type: ${typeof parsed}`);
|
|
221
460
|
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
kind: Json.Kind.Array,
|
|
464
|
+
...base,
|
|
465
|
+
values
|
|
466
|
+
} satisfies Json.Array as Json.Array;
|
|
222
467
|
}
|
|
223
468
|
|
|
224
|
-
private
|
|
469
|
+
private parseMember(pendingTrivia: string): Json.Member {
|
|
470
|
+
const keyPrefix = pendingTrivia + this.consumeTrivia();
|
|
471
|
+
const key = this.parseKey({
|
|
472
|
+
id: randomId(),
|
|
473
|
+
prefix: space(keyPrefix),
|
|
474
|
+
markers: emptyMarkers
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const afterKey = this.consumeTrivia();
|
|
478
|
+
if (this.token !== Token.ColonToken) {
|
|
479
|
+
throw new Error(`Expected ':' at offset ${this.tokenOffset}, found ${TokenNames[this.token] || this.token}`);
|
|
480
|
+
}
|
|
481
|
+
this.advance(); // consume ':'
|
|
482
|
+
|
|
483
|
+
const value = this.parseValue() as Json.Value;
|
|
484
|
+
|
|
225
485
|
return {
|
|
226
486
|
kind: Json.Kind.Member,
|
|
227
487
|
id: randomId(),
|
|
@@ -230,10 +490,47 @@ class ParseJsonReader extends ParserSourceReader {
|
|
|
230
490
|
key: {
|
|
231
491
|
kind: Json.Kind.RightPadded,
|
|
232
492
|
markers: emptyMarkers,
|
|
233
|
-
element:
|
|
234
|
-
after: space(
|
|
493
|
+
element: key as Json.Key,
|
|
494
|
+
after: space(afterKey)
|
|
235
495
|
} satisfies Json.RightPadded<Json.Key> as Json.RightPadded<Json.Key>,
|
|
236
|
-
value
|
|
496
|
+
value
|
|
237
497
|
} satisfies Json.Member as Json.Member;
|
|
238
498
|
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Parses a key which is a string literal in standard JSON/JSONC.
|
|
502
|
+
* Note: jsonc-parser doesn't support JSON5 unquoted identifiers.
|
|
503
|
+
*/
|
|
504
|
+
private parseKey(base: { id: string; prefix: Json.Space; markers: typeof emptyMarkers }): Json.Literal {
|
|
505
|
+
if (this.token !== Token.StringLiteral) {
|
|
506
|
+
throw new Error(`Expected string key at offset ${this.tokenOffset}, found ${TokenNames[this.token] || this.token}`);
|
|
507
|
+
}
|
|
508
|
+
return this.parseStringLiteral(base);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
private parseStringLiteral(base: { id: string; prefix: Json.Space; markers: typeof emptyMarkers }): Json.Literal {
|
|
512
|
+
const source = this.source.slice(this.tokenOffset, this.tokenOffset + this.tokenLength);
|
|
513
|
+
const value = this.tokenValue;
|
|
514
|
+
this.advance();
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
kind: Json.Kind.Literal,
|
|
518
|
+
...base,
|
|
519
|
+
source,
|
|
520
|
+
value
|
|
521
|
+
} satisfies Json.Literal as Json.Literal;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private parseNumericLiteral(base: { id: string; prefix: Json.Space; markers: typeof emptyMarkers }): Json.Literal {
|
|
525
|
+
const source = this.source.slice(this.tokenOffset, this.tokenOffset + this.tokenLength);
|
|
526
|
+
const value = parseFloat(source);
|
|
527
|
+
this.advance();
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
kind: Json.Kind.Literal,
|
|
531
|
+
...base,
|
|
532
|
+
source,
|
|
533
|
+
value
|
|
534
|
+
} satisfies Json.Literal as Json.Literal;
|
|
535
|
+
}
|
|
239
536
|
}
|