@openrewrite/rewrite 8.91.2 → 8.91.4

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.
Files changed (130) hide show
  1. package/dist/execution.d.ts +8 -0
  2. package/dist/execution.d.ts.map +1 -1
  3. package/dist/execution.js +31 -0
  4. package/dist/execution.js.map +1 -1
  5. package/dist/javascript/add-import.d.ts +64 -9
  6. package/dist/javascript/add-import.d.ts.map +1 -1
  7. package/dist/javascript/add-import.js +398 -71
  8. package/dist/javascript/add-import.js.map +1 -1
  9. package/dist/javascript/amd.d.ts +11 -1
  10. package/dist/javascript/amd.d.ts.map +1 -1
  11. package/dist/javascript/amd.js +43 -9
  12. package/dist/javascript/amd.js.map +1 -1
  13. package/dist/javascript/binding.d.ts +10 -2
  14. package/dist/javascript/binding.d.ts.map +1 -1
  15. package/dist/javascript/binding.js +44 -71
  16. package/dist/javascript/binding.js.map +1 -1
  17. package/dist/javascript/comparator.d.ts +2 -0
  18. package/dist/javascript/comparator.d.ts.map +1 -1
  19. package/dist/javascript/comparator.js +5 -1
  20. package/dist/javascript/comparator.js.map +1 -1
  21. package/dist/javascript/format/format.d.ts +5 -2
  22. package/dist/javascript/format/format.d.ts.map +1 -1
  23. package/dist/javascript/format/format.js +51 -11
  24. package/dist/javascript/format/format.js.map +1 -1
  25. package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts +19 -0
  26. package/dist/javascript/format/minimum-viable-spacing-visitor.d.ts.map +1 -1
  27. package/dist/javascript/format/minimum-viable-spacing-visitor.js +180 -7
  28. package/dist/javascript/format/minimum-viable-spacing-visitor.js.map +1 -1
  29. package/dist/javascript/format/prettier-format.d.ts +2 -1
  30. package/dist/javascript/format/prettier-format.d.ts.map +1 -1
  31. package/dist/javascript/format/prettier-format.js +33 -23
  32. package/dist/javascript/format/prettier-format.js.map +1 -1
  33. package/dist/javascript/index.d.ts +3 -3
  34. package/dist/javascript/index.d.ts.map +1 -1
  35. package/dist/javascript/index.js +21 -1
  36. package/dist/javascript/index.js.map +1 -1
  37. package/dist/javascript/parser-utils.d.ts.map +1 -1
  38. package/dist/javascript/parser-utils.js +5 -9
  39. package/dist/javascript/parser-utils.js.map +1 -1
  40. package/dist/javascript/parser.d.ts +3 -2
  41. package/dist/javascript/parser.d.ts.map +1 -1
  42. package/dist/javascript/parser.js +202 -117
  43. package/dist/javascript/parser.js.map +1 -1
  44. package/dist/javascript/print.d.ts +1 -0
  45. package/dist/javascript/print.d.ts.map +1 -1
  46. package/dist/javascript/print.js +9 -0
  47. package/dist/javascript/print.js.map +1 -1
  48. package/dist/javascript/recipes/change-import.d.ts +6 -3
  49. package/dist/javascript/recipes/change-import.d.ts.map +1 -1
  50. package/dist/javascript/recipes/change-import.js +17 -277
  51. package/dist/javascript/recipes/change-import.js.map +1 -1
  52. package/dist/javascript/rpc.js +2 -0
  53. package/dist/javascript/rpc.js.map +1 -1
  54. package/dist/javascript/scope.d.ts +40 -7
  55. package/dist/javascript/scope.d.ts.map +1 -1
  56. package/dist/javascript/scope.js +182 -30
  57. package/dist/javascript/scope.js.map +1 -1
  58. package/dist/javascript/templating/bindings.js +2 -20
  59. package/dist/javascript/templating/bindings.js.map +1 -1
  60. package/dist/javascript/templating/comparator.d.ts +2 -0
  61. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  62. package/dist/javascript/templating/comparator.js +25 -0
  63. package/dist/javascript/templating/comparator.js.map +1 -1
  64. package/dist/javascript/templating/engine.d.ts +5 -0
  65. package/dist/javascript/templating/engine.d.ts.map +1 -1
  66. package/dist/javascript/templating/engine.js +30 -12
  67. package/dist/javascript/templating/engine.js.map +1 -1
  68. package/dist/javascript/templating/template.d.ts.map +1 -1
  69. package/dist/javascript/templating/template.js +6 -0
  70. package/dist/javascript/templating/template.js.map +1 -1
  71. package/dist/javascript/templating/types.d.ts +5 -0
  72. package/dist/javascript/templating/types.d.ts.map +1 -1
  73. package/dist/javascript/tree.d.ts +1 -0
  74. package/dist/javascript/tree.d.ts.map +1 -1
  75. package/dist/javascript/tree.js.map +1 -1
  76. package/dist/javascript/tsconfig.d.ts +32 -0
  77. package/dist/javascript/tsconfig.d.ts.map +1 -0
  78. package/dist/javascript/tsconfig.js +205 -0
  79. package/dist/javascript/tsconfig.js.map +1 -0
  80. package/dist/javascript/type-mapping.d.ts.map +1 -1
  81. package/dist/javascript/type-mapping.js +14 -0
  82. package/dist/javascript/type-mapping.js.map +1 -1
  83. package/dist/javascript/visitor.d.ts.map +1 -1
  84. package/dist/javascript/visitor.js +1 -0
  85. package/dist/javascript/visitor.js.map +1 -1
  86. package/dist/parser.d.ts +6 -0
  87. package/dist/parser.d.ts.map +1 -1
  88. package/dist/parser.js +23 -1
  89. package/dist/parser.js.map +1 -1
  90. package/dist/print.d.ts.map +1 -1
  91. package/dist/print.js +5 -2
  92. package/dist/print.js.map +1 -1
  93. package/dist/rewrite-javascript-version.txt +1 -1
  94. package/dist/rpc/request/parse-project.d.ts +11 -1
  95. package/dist/rpc/request/parse-project.d.ts.map +1 -1
  96. package/dist/rpc/request/parse-project.js +7 -2
  97. package/dist/rpc/request/parse-project.js.map +1 -1
  98. package/dist/rpc/request/parse.d.ts +13 -1
  99. package/dist/rpc/request/parse.d.ts.map +1 -1
  100. package/dist/rpc/request/parse.js +8 -2
  101. package/dist/rpc/request/parse.js.map +1 -1
  102. package/package.json +1 -1
  103. package/src/execution.ts +23 -0
  104. package/src/javascript/add-import.ts +449 -78
  105. package/src/javascript/amd.ts +49 -10
  106. package/src/javascript/binding.ts +60 -74
  107. package/src/javascript/comparator.ts +6 -1
  108. package/src/javascript/format/format.ts +56 -12
  109. package/src/javascript/format/minimum-viable-spacing-visitor.ts +196 -7
  110. package/src/javascript/format/prettier-format.ts +35 -25
  111. package/src/javascript/index.ts +10 -3
  112. package/src/javascript/parser-utils.ts +5 -11
  113. package/src/javascript/parser.ts +167 -62
  114. package/src/javascript/print.ts +10 -0
  115. package/src/javascript/recipes/change-import.ts +19 -349
  116. package/src/javascript/rpc.ts +2 -0
  117. package/src/javascript/scope.ts +210 -31
  118. package/src/javascript/templating/bindings.ts +2 -25
  119. package/src/javascript/templating/comparator.ts +29 -2
  120. package/src/javascript/templating/engine.ts +41 -14
  121. package/src/javascript/templating/template.ts +6 -0
  122. package/src/javascript/templating/types.ts +6 -0
  123. package/src/javascript/tree.ts +1 -0
  124. package/src/javascript/tsconfig.ts +177 -0
  125. package/src/javascript/type-mapping.ts +15 -0
  126. package/src/javascript/visitor.ts +1 -0
  127. package/src/parser.ts +24 -1
  128. package/src/print.ts +5 -2
  129. package/src/rpc/request/parse-project.ts +6 -2
  130. package/src/rpc/request/parse.ts +7 -2
@@ -46,6 +46,7 @@ import {
46
46
  TextSpan
47
47
  } from "./parser-utils";
48
48
  import {JavaScriptTypeMapping} from "./type-mapping";
49
+ import {TsConfigResolver} from "./tsconfig";
49
50
  import {create as produce} from "mutative";
50
51
  import ComputedPropertyName = JS.ComputedPropertyName;
51
52
  import Attribute = JSX.Attribute;
@@ -79,11 +80,20 @@ function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
79
80
  }
80
81
  }
81
82
 
83
+ /**
84
+ * TypeScript's public typings model valid syntax only, so slots its parser still fills for invalid code —
85
+ * an initializer on a type member, a modifier on an object literal member — are missing from them.
86
+ */
87
+ type WithInvalidSyntaxSlots<T> = T & {
88
+ initializer?: ts.Expression;
89
+ modifiers?: ts.NodeArray<ts.ModifierLike>;
90
+ };
91
+
82
92
  export class JavaScriptParser extends Parser {
83
93
 
84
- private readonly compilerOptions: ts.CompilerOptions;
94
+ private readonly tsConfig: TsConfigResolver;
85
95
  private readonly styles?: NamedStyles[];
86
- private oldProgram?: ts.Program;
96
+ private readonly oldPrograms = new Map<string, ts.Program>();
87
97
  private readonly sourceFileCache?: Map<string, ts.SourceFile>;
88
98
 
89
99
  constructor(
@@ -96,7 +106,7 @@ export class JavaScriptParser extends Parser {
96
106
  }: JavaScriptParserOptions = {},
97
107
  ) {
98
108
  super({ctx, relativeTo});
99
- this.compilerOptions = {
109
+ const defaultCompilerOptions: ts.CompilerOptions = {
100
110
  target: ts.ScriptTarget.Latest,
101
111
  // Bundler matches `exports` conditions leniently, so packages that publish types only under
102
112
  // an `import` condition resolve; it pairs with `preserve`, which `commonjs` cannot (TS5095).
@@ -123,6 +133,7 @@ export class JavaScriptParser extends Parser {
123
133
  types: types ?? ["*"],
124
134
  baseUrl: relativeTo || process.cwd()
125
135
  };
136
+ this.tsConfig = new TsConfigResolver(defaultCompilerOptions, relativeTo);
126
137
  this.styles = styles;
127
138
  this.sourceFileCache = sourceFileCache;
128
139
  }
@@ -199,12 +210,14 @@ export class JavaScriptParser extends Parser {
199
210
  // noinspection JSUnusedGlobalSymbols
200
211
  reset(): this {
201
212
  this.sourceFileCache && this.sourceFileCache.clear();
202
- this.oldProgram = undefined;
213
+ this.oldPrograms.clear();
203
214
  return this;
204
215
  }
205
216
 
206
217
  override async* parse(...inputs: ParserInput[]): AsyncGenerator<SourceFile> {
207
218
  const inputFiles = new Map<SourcePath, ParserInput>();
219
+ const projectOf = new Map<SourcePath, string>();
220
+ const projects = new Map<string, { options: ts.CompilerOptions, rootNames: SourcePath[] }>();
208
221
 
209
222
  // Populate inputFiles map and remove from cache if necessary
210
223
  for (const input of inputs) {
@@ -219,8 +232,79 @@ export class JavaScriptParser extends Parser {
219
232
  this.sourceFileCache && this.sourceFileCache.delete(normalizedSourcePath);
220
233
  }
221
234
 
222
- // Create a new CompilerHost within parseInputs
223
- const host = ts.createCompilerHost(this.compilerOptions);
235
+ // Packages in a monorepo each configure their own module resolution.
236
+ for (const normalizedSourcePath of inputFiles.keys()) {
237
+ const {configFilePath, options} = this.tsConfig.forFile(normalizedSourcePath);
238
+ const projectKey = configFilePath ?? "";
239
+ projectOf.set(normalizedSourcePath, projectKey);
240
+ let project = projects.get(projectKey);
241
+ if (!project) {
242
+ projects.set(projectKey, project = {options, rootNames: []});
243
+ }
244
+ project.rootNames.push(normalizedSourcePath);
245
+ }
246
+
247
+ // Module resolution probes a directory before the files in it, so a directory holding
248
+ // nothing but in-memory inputs has to answer too, or those inputs are unreachable.
249
+ const inputDirectories = new Set<string>();
250
+ for (const sourcePath of inputFiles.keys()) {
251
+ for (let dir = path.dirname(sourcePath); !inputDirectories.has(dir); dir = path.dirname(dir)) {
252
+ inputDirectories.add(dir);
253
+ }
254
+ }
255
+
256
+ const compiled = new Map<string, { program: ts.Program, typeMapping: JavaScriptTypeMapping }>();
257
+ for (const [projectKey, {options, rootNames}] of projects) {
258
+ // TypeScript carries the previous program's bound files forward when the root paths match,
259
+ // so a parser held across parses of one path costs a fraction of a freshly built one.
260
+ const program = ts.createProgram(rootNames, options,
261
+ this.createCompilerHost(options, inputFiles, inputDirectories), this.oldPrograms.get(projectKey));
262
+ this.oldPrograms.set(projectKey, program);
263
+
264
+ // A JavaScriptTypeMapping deduplicates by `ts.Type.id`, which is only unique within
265
+ // the TypeChecker that issued it, so each program needs its own.
266
+ compiled.set(projectKey, {program, typeMapping: new JavaScriptTypeMapping(program.getTypeChecker(), this.relativeTo)});
267
+ }
268
+
269
+ for (const [sourcePath, input] of inputFiles) {
270
+ const {program, typeMapping} = compiled.get(projectOf.get(sourcePath)!)!;
271
+ const filePath = parserInputFile(input);
272
+ const sourceFile = program.getSourceFile(filePath);
273
+ if (!sourceFile) {
274
+ yield this.error(input, new Error('Parser returned undefined'));
275
+ continue;
276
+ }
277
+
278
+ if (hasFlowAnnotation(sourceFile)) {
279
+ yield this.error(input, new FlowSyntaxNotSupportedError("Flow syntax not supported"));
280
+ continue;
281
+ }
282
+
283
+ const syntaxErrors = checkSyntaxErrors(program, sourceFile);
284
+ if (syntaxErrors.length > 0) {
285
+ let errors = syntaxErrors.map(e => `${e[0]} [${e[1]}]`).join('; ');
286
+ yield this.error(input, new SyntaxError(`Compiler error(s): ${errors}`));
287
+ continue;
288
+ }
289
+
290
+ try {
291
+ yield await this.requirePrintEqualsInput(produce(
292
+ new JavaScriptParserVisitor(sourceFile, this.relativePath(input), typeMapping)
293
+ .visit(sourceFile) as SourceFile,
294
+ draft => {
295
+ if (this.styles) {
296
+ draft.markers = this.styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
297
+ }
298
+ }), input, escapeLoneSurrogates(sourceFile.getFullText()));
299
+ } catch (error) {
300
+ yield this.error(input, error instanceof Error ? error : new Error('Parser threw unknown error: ' + error));
301
+ }
302
+ }
303
+ }
304
+
305
+ private createCompilerHost(compilerOptions: ts.CompilerOptions, inputFiles: Map<SourcePath, ParserInput>,
306
+ inputDirectories: Set<string>): ts.CompilerHost {
307
+ const host = ts.createCompilerHost(compilerOptions);
224
308
 
225
309
  // Set the current directory for module resolution
226
310
  if (this.relativeTo) {
@@ -231,8 +315,13 @@ export class JavaScriptParser extends Parser {
231
315
  // Override getSourceFile
232
316
  host.getSourceFile = (fileName, languageVersion, onError) => {
233
317
  const normalizedFileName = path.normalize(fileName);
318
+ // A file's module format is baked in when it is created and follows from the requesting
319
+ // project's `moduleResolution`, so it belongs in the key of a cache several projects share.
320
+ const cacheKey = typeof languageVersion === 'number' ?
321
+ normalizedFileName :
322
+ `${languageVersion.impliedNodeFormat ?? ''}\0${normalizedFileName}`;
234
323
  // Check if the SourceFile is in the cache
235
- let sourceFile = this.sourceFileCache && this.sourceFileCache.get(normalizedFileName);
324
+ let sourceFile = this.sourceFileCache && this.sourceFileCache.get(cacheKey);
236
325
  if (sourceFile) {
237
326
  return sourceFile;
238
327
  }
@@ -267,7 +356,7 @@ export class JavaScriptParser extends Parser {
267
356
  sourceFile = ts.createSourceFile(normalizedFileName, sourceText, sourceFileOptions, true, scriptKind);
268
357
  // Cache the SourceFile if it's a dependency
269
358
  if (!input && this.sourceFileCache) {
270
- this.sourceFileCache.set(normalizedFileName, sourceFile);
359
+ this.sourceFileCache.set(cacheKey, sourceFile);
271
360
  }
272
361
  return sourceFile;
273
362
  }
@@ -282,6 +371,11 @@ export class JavaScriptParser extends Parser {
282
371
  return inputFiles.has(normalizedFileName) || ts.sys.fileExists(normalizedFileName);
283
372
  };
284
373
 
374
+ host.directoryExists = (directoryName) => {
375
+ const normalizedDirectoryName = path.normalize(directoryName);
376
+ return inputDirectories.has(normalizedDirectoryName) || ts.sys.directoryExists(normalizedDirectoryName);
377
+ };
378
+
285
379
  // Override readFile
286
380
  host.readFile = (fileName) => {
287
381
  const normalizedFileName = path.normalize(fileName);
@@ -292,7 +386,7 @@ export class JavaScriptParser extends Parser {
292
386
  // Custom module resolution to handle in-memory imports
293
387
  // This is required because TypeScript's default module resolution looks for files on disk,
294
388
  // but our source files only exist in memory (in the inputFiles map)
295
- host.resolveModuleNameLiterals = (moduleLiterals, containingFile) => {
389
+ host.resolveModuleNameLiterals = (moduleLiterals, containingFile, _redirectedReference, _options, containingSourceFile) => {
296
390
  const resolvedModules: ts.ResolvedModuleWithFailedLookupLocations[] = [];
297
391
  const normalizedFileName = path.normalize(containingFile);
298
392
  const containingDir = path.dirname(normalizedFileName);
@@ -332,12 +426,18 @@ export class JavaScriptParser extends Parser {
332
426
  }
333
427
  }
334
428
 
335
- // Fall back to TypeScript's default resolution for node_modules and absolute paths
429
+ // Fall back to TypeScript's default resolution for node_modules and absolute paths.
430
+ // Passing the usage location's mode keeps export conditions tied to the module kind
431
+ // pinned above, so every project reads the same branch of a dual package whatever
432
+ // `moduleResolution` it states.
336
433
  const result = ts.resolveModuleName(
337
434
  moduleName,
338
435
  normalizedFileName,
339
- this.compilerOptions,
340
- host
436
+ compilerOptions,
437
+ host,
438
+ undefined,
439
+ undefined,
440
+ ts.getModeForUsageLocation(containingSourceFile, moduleLiteral, compilerOptions)
341
441
  );
342
442
 
343
443
  resolvedModules.push(result);
@@ -345,52 +445,7 @@ export class JavaScriptParser extends Parser {
345
445
  return resolvedModules;
346
446
  };
347
447
 
348
- // TypeScript carries the previous program's bound files forward when the root paths match,
349
- // so a parser held across parses of one path costs a fraction of a freshly built one.
350
- const program = ts.createProgram([...inputFiles.keys()], this.compilerOptions, host, this.oldProgram);
351
-
352
- // Update the oldProgram reference
353
- this.oldProgram = program;
354
-
355
- // Create a single JavaScriptTypeMapping instance to be shared across all files in this parse batch.
356
- // This ensures that TypeScript types with the same type.id map to the same Type instance,
357
- // preventing duplicate Type.Class, Type.Parameterized, etc. instances.
358
- const typeChecker = program.getTypeChecker();
359
- const typeMapping = new JavaScriptTypeMapping(typeChecker, this.relativeTo);
360
-
361
- for (const input of inputFiles.values()) {
362
- const filePath = parserInputFile(input);
363
- const sourceFile = program.getSourceFile(filePath);
364
- if (!sourceFile) {
365
- yield this.error(input, new Error('Parser returned undefined'));
366
- continue;
367
- }
368
-
369
- if (hasFlowAnnotation(sourceFile)) {
370
- yield this.error(input, new FlowSyntaxNotSupportedError("Flow syntax not supported"));
371
- continue;
372
- }
373
-
374
- const syntaxErrors = checkSyntaxErrors(program, sourceFile);
375
- if (syntaxErrors.length > 0) {
376
- let errors = syntaxErrors.map(e => `${e[0]} [${e[1]}]`).join('; ');
377
- yield this.error(input, new SyntaxError(`Compiler error(s): ${errors}`));
378
- continue;
379
- }
380
-
381
- try {
382
- yield produce(
383
- new JavaScriptParserVisitor(sourceFile, this.relativePath(input), typeMapping)
384
- .visit(sourceFile) as SourceFile,
385
- draft => {
386
- if (this.styles) {
387
- draft.markers = this.styles.reduce((m, s) => replaceMarkerByKind(m, s), draft.markers);
388
- }
389
- });
390
- } catch (error) {
391
- yield this.error(input, error instanceof Error ? error : new Error('Parser threw unknown error: ' + error));
392
- }
393
- }
448
+ return host;
394
449
  }
395
450
  }
396
451
 
@@ -573,7 +628,12 @@ export class JavaScriptParserVisitor {
573
628
  | ts.FunctionDeclaration | ts.ParameterDeclaration | ts.MethodDeclaration | ts.EnumDeclaration | ts.InterfaceDeclaration
574
629
  | ts.PropertySignature | ts.ConstructorDeclaration | ts.ModuleDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration
575
630
  | ts.ArrowFunction | ts.IndexSignatureDeclaration | ts.TypeAliasDeclaration | ts.ExportDeclaration | ts.ExportAssignment | ts.FunctionExpression
576
- | ts.ConstructorTypeNode | ts.TypeParameterDeclaration | ts.ImportDeclaration | ts.ImportEqualsDeclaration): J.Modifier[] {
631
+ | ts.ConstructorTypeNode | ts.TypeParameterDeclaration | ts.ImportDeclaration | ts.ImportEqualsDeclaration
632
+ | ts.PropertyAssignment | ts.ShorthandPropertyAssignment): J.Modifier[] {
633
+ if (ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node)) {
634
+ const modifiers = (node as WithInvalidSyntaxSlots<typeof node>).modifiers;
635
+ return modifiers ? modifiers.filter(ts.isModifier).map(this.mapModifier) : [];
636
+ }
577
637
  if (ts.isVariableStatement(node) || ts.isModuleDeclaration(node) || ts.isClassDeclaration(node) || ts.isEnumDeclaration(node)
578
638
  || ts.isInterfaceDeclaration(node) || ts.isPropertyDeclaration(node) || ts.isPropertySignature(node) || ts.isParameter(node)
579
639
  || ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isArrowFunction(node)
@@ -1175,6 +1235,8 @@ export class JavaScriptParserVisitor {
1175
1235
  visitPropertySignature(node: ts.PropertySignature): J.VariableDeclarations {
1176
1236
  // FIXME We are mapping the literals in things like type ascii = { " ": 32; "!": 33; } to
1177
1237
  // named variables, which is not a good use of this construct.
1238
+
1239
+ const initializer = (node as WithInvalidSyntaxSlots<ts.PropertySignature>).initializer;
1178
1240
  return {
1179
1241
  kind: J.Kind.VariableDeclarations,
1180
1242
  id: randomId(),
@@ -1193,6 +1255,7 @@ export class JavaScriptParserVisitor {
1193
1255
  draft.markers = this.maybeAddOptionalMarker(draft, node);
1194
1256
  }) as VariableDeclarator,
1195
1257
  dimensionsAfterName: [],
1258
+ initializer: initializer && this.leftPadded(this.prefix(node.getChildAt(node.getChildren().indexOf(initializer) - 1)), this.visit(initializer)),
1196
1259
  variableType: this.mapVariableType(node)
1197
1260
  },
1198
1261
  emptySpace
@@ -1355,7 +1418,8 @@ export class JavaScriptParserVisitor {
1355
1418
 
1356
1419
  private mapTypeInfo(node: ts.MethodDeclaration | ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration
1357
1420
  | ts.PropertySignature | ts.MethodSignature | ts.ArrowFunction | ts.CallSignatureDeclaration | ts.GetAccessorDeclaration
1358
- | ts.FunctionDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.NamedTupleMember): JS.TypeInfo | undefined {
1421
+ | ts.FunctionDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.NamedTupleMember
1422
+ | ts.ConstructorDeclaration | ts.SetAccessorDeclaration): JS.TypeInfo | undefined {
1359
1423
  return node.type && {
1360
1424
  kind: JS.Kind.TypeInfo,
1361
1425
  id: randomId(),
@@ -1397,6 +1461,7 @@ export class JavaScriptParserVisitor {
1397
1461
  leadingAnnotations: this.mapDecorators(node),
1398
1462
  modifiers: this.mapModifiers(node),
1399
1463
  nameAnnotations: [],
1464
+ returnTypeExpression: this.mapTypeInfo(node),
1400
1465
  name: {...this.mapIdentifier(constructorKeyword, constructorKeyword.getText(), false), type: methodType},
1401
1466
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1402
1467
  dimensionsAfterName: [],
@@ -1416,6 +1481,7 @@ export class JavaScriptParserVisitor {
1416
1481
  markers: emptyMarkers,
1417
1482
  leadingAnnotations: this.mapDecorators(node),
1418
1483
  modifiers: this.mapModifiers(node),
1484
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1419
1485
  returnTypeExpression: this.mapTypeInfo(node),
1420
1486
  name: name as ComputedPropertyName,
1421
1487
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -1431,6 +1497,7 @@ export class JavaScriptParserVisitor {
1431
1497
  markers: emptyMarkers,
1432
1498
  leadingAnnotations: this.mapDecorators(node),
1433
1499
  modifiers: this.mapModifiers(node),
1500
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1434
1501
  returnTypeExpression: this.mapTypeInfo(node),
1435
1502
  nameAnnotations: [],
1436
1503
  name: name as J.Identifier,
@@ -1452,6 +1519,8 @@ export class JavaScriptParserVisitor {
1452
1519
  markers: emptyMarkers,
1453
1520
  leadingAnnotations: this.mapDecorators(node),
1454
1521
  modifiers: this.mapModifiers(node),
1522
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1523
+ returnTypeExpression: this.mapTypeInfo(node),
1455
1524
  name: name as ComputedPropertyName,
1456
1525
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
1457
1526
  body: node.body && this.convert<J.Block>(node.body),
@@ -1466,6 +1535,8 @@ export class JavaScriptParserVisitor {
1466
1535
  markers: emptyMarkers,
1467
1536
  leadingAnnotations: this.mapDecorators(node),
1468
1537
  modifiers: this.mapModifiers(node),
1538
+ typeParameters: node.typeParameters && this.mapTypeParametersAsObject(node),
1539
+ returnTypeExpression: this.mapTypeInfo(node),
1469
1540
  nameAnnotations: [],
1470
1541
  name: name as J.Identifier,
1471
1542
  parameters: this.mapCommaSeparatedList(this.getParameterListNodes(node)),
@@ -4219,6 +4290,7 @@ export class JavaScriptParserVisitor {
4219
4290
  id: randomId(),
4220
4291
  prefix: this.prefix(node),
4221
4292
  markers: emptyMarkers,
4293
+ modifiers: this.mapModifiers(node),
4222
4294
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4223
4295
  assigmentToken: JS.PropertyAssignment.Token.Colon,
4224
4296
  initializer: this.visit(node.initializer)
@@ -4231,6 +4303,7 @@ export class JavaScriptParserVisitor {
4231
4303
  id: randomId(),
4232
4304
  prefix: this.prefix(node),
4233
4305
  markers: emptyMarkers,
4306
+ modifiers: this.mapModifiers(node),
4234
4307
  name: this.rightPadded(this.visit(node.name), this.suffix(node.name)),
4235
4308
  assigmentToken: JS.PropertyAssignment.Token.Equals,
4236
4309
  initializer: node.objectAssignmentInitializer && this.visit(node.objectAssignmentInitializer)
@@ -4654,7 +4727,8 @@ export class JavaScriptParserVisitor {
4654
4727
  }
4655
4728
 
4656
4729
  private mapTypeParametersAsObject(node: ts.MethodDeclaration | ts.MethodSignature | ts.FunctionDeclaration
4657
- | ts.CallSignatureDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.TypeAliasDeclaration | ts.FunctionTypeNode | ts.ConstructorTypeNode): J.TypeParameters | undefined {
4730
+ | ts.CallSignatureDeclaration | ts.ConstructSignatureDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.TypeAliasDeclaration | ts.FunctionTypeNode | ts.ConstructorTypeNode
4731
+ | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration): J.TypeParameters | undefined {
4658
4732
  const typeParameters = node.typeParameters;
4659
4733
  if (!typeParameters) return undefined;
4660
4734
 
@@ -4806,6 +4880,8 @@ class FlowSyntaxNotSupportedError extends SyntaxError {
4806
4880
 
4807
4881
  const SURR_FIRST = 0xD800;
4808
4882
  const SURR_LAST = 0xDFFF;
4883
+ const HIGH_SURR_LAST = 0xDBFF;
4884
+ const LOW_SURR_FIRST = 0xDC00;
4809
4885
 
4810
4886
  /**
4811
4887
  * Extracts invalid UTF-16 surrogate pairs from a string literal's value source.
@@ -4819,6 +4895,26 @@ const SURR_LAST = 0xDFFF;
4819
4895
  * 1. Unicode escape sequences (\uXXXX) where XXXX is in the surrogate range
4820
4896
  * 2. Raw surrogate characters in the source (character codes 0xD800-0xDFFF)
4821
4897
  */
4898
+ /** The form {@link extractSurrogateEscapes} can hold, against which print idempotence is measured. */
4899
+ function escapeLoneSurrogates(source: string): string {
4900
+ let escaped = '';
4901
+ for (let j = 0; j < source.length; j++) {
4902
+ const charCode = source.charCodeAt(j);
4903
+ if (charCode >= SURR_FIRST && charCode <= SURR_LAST) {
4904
+ const next = source.charCodeAt(j + 1);
4905
+ if (charCode <= HIGH_SURR_LAST && next >= LOW_SURR_FIRST && next <= SURR_LAST) {
4906
+ escaped += source.charAt(j) + source.charAt(j + 1);
4907
+ j++;
4908
+ continue;
4909
+ }
4910
+ escaped += "\\u" + charCode.toString(16).toUpperCase().padStart(4, '0');
4911
+ continue;
4912
+ }
4913
+ escaped += source.charAt(j);
4914
+ }
4915
+ return escaped;
4916
+ }
4917
+
4822
4918
  function extractSurrogateEscapes(valueSource: string): { cleanedSource: string, unicodeEscapes: J.LiteralUnicodeEscape[] } {
4823
4919
  const unicodeEscapes: J.LiteralUnicodeEscape[] = [];
4824
4920
  let cleanedSource = '';
@@ -4842,8 +4938,17 @@ function extractSurrogateEscapes(valueSource: string): { cleanedSource: string,
4842
4938
  }
4843
4939
  }
4844
4940
 
4845
- // Check for raw surrogate characters in the source
4941
+ // A raw surrogate pair spells one code point — an emoji, say — and is carried as itself. A lone
4942
+ // surrogate is not text on its own and does not survive serialization (#6599), so it is held as a
4943
+ // `\uXXXX` escape and prints back as one.
4846
4944
  if (charCode >= SURR_FIRST && charCode <= SURR_LAST) {
4945
+ const next = valueSource.charCodeAt(j + 1);
4946
+ if (charCode <= HIGH_SURR_LAST && next >= LOW_SURR_FIRST && next <= SURR_LAST) {
4947
+ cleanedSource += c + valueSource.charAt(j + 1);
4948
+ cleanedIndex += 2;
4949
+ j++;
4950
+ continue;
4951
+ }
4847
4952
  const codePoint = charCode.toString(16).toUpperCase().padStart(4, '0');
4848
4953
  unicodeEscapes.push({ valueSourceIndex: cleanedIndex, codePoint });
4849
4954
  continue;
@@ -950,6 +950,13 @@ export class JavaScriptPrinter extends JavaScriptVisitor<PrintOutputCapture> {
950
950
  return typeDeclaration;
951
951
  }
952
952
 
953
+ override async visitUnknownSource(source: J.UnknownSource, p: PrintOutputCapture): Promise<J | undefined> {
954
+ await this.beforeSyntax(source, p);
955
+ p.append(source.text);
956
+ await this.afterSyntax(source, p);
957
+ return source;
958
+ }
959
+
953
960
  override async visitLiteralType(literalType: JS.LiteralType, p: PrintOutputCapture): Promise<J | undefined> {
954
961
  await this.beforeSyntax(literalType, p);
955
962
  await this.visit(literalType.literal, p);
@@ -1413,6 +1420,9 @@ export class JavaScriptPrinter extends JavaScriptVisitor<PrintOutputCapture> {
1413
1420
  override async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, p: PrintOutputCapture): Promise<J | undefined> {
1414
1421
  await this.beforeSyntax(propertyAssignment, p);
1415
1422
 
1423
+ for (const m of propertyAssignment.modifiers) {
1424
+ await this.visitModifier(m, p);
1425
+ }
1416
1426
  await this.visitRightPadded(propertyAssignment.name, p);
1417
1427
 
1418
1428
  if (propertyAssignment.initializer) {