@openrewrite/rewrite 8.91.0 → 8.91.2

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 (153) hide show
  1. package/dist/java/formatting-utils.d.ts +8 -5
  2. package/dist/java/formatting-utils.d.ts.map +1 -1
  3. package/dist/java/formatting-utils.js +14 -10
  4. package/dist/java/formatting-utils.js.map +1 -1
  5. package/dist/java/type.d.ts +2 -0
  6. package/dist/java/type.d.ts.map +1 -1
  7. package/dist/java/type.js +5 -0
  8. package/dist/java/type.js.map +1 -1
  9. package/dist/javascript/add-import.d.ts +96 -43
  10. package/dist/javascript/add-import.d.ts.map +1 -1
  11. package/dist/javascript/add-import.js +668 -236
  12. package/dist/javascript/add-import.js.map +1 -1
  13. package/dist/javascript/amd.d.ts +138 -0
  14. package/dist/javascript/amd.d.ts.map +1 -0
  15. package/dist/javascript/amd.js +768 -0
  16. package/dist/javascript/amd.js.map +1 -0
  17. package/dist/javascript/assertions.d.ts +7 -0
  18. package/dist/javascript/assertions.d.ts.map +1 -1
  19. package/dist/javascript/assertions.js +31 -5
  20. package/dist/javascript/assertions.js.map +1 -1
  21. package/dist/javascript/binding.d.ts +94 -0
  22. package/dist/javascript/binding.d.ts.map +1 -0
  23. package/dist/javascript/binding.js +335 -0
  24. package/dist/javascript/binding.js.map +1 -0
  25. package/dist/javascript/comparator.d.ts +5 -0
  26. package/dist/javascript/comparator.d.ts.map +1 -1
  27. package/dist/javascript/comparator.js +34 -9
  28. package/dist/javascript/comparator.js.map +1 -1
  29. package/dist/javascript/format/format.d.ts.map +1 -1
  30. package/dist/javascript/format/format.js +8 -9
  31. package/dist/javascript/format/format.js.map +1 -1
  32. package/dist/javascript/format/tabs-and-indents-visitor.d.ts +2 -0
  33. package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -1
  34. package/dist/javascript/format/tabs-and-indents-visitor.js +17 -11
  35. package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
  36. package/dist/javascript/index.d.ts +8 -1
  37. package/dist/javascript/index.d.ts.map +1 -1
  38. package/dist/javascript/index.js +26 -1
  39. package/dist/javascript/index.js.map +1 -1
  40. package/dist/javascript/node-resolution-result.d.ts +19 -1
  41. package/dist/javascript/node-resolution-result.d.ts.map +1 -1
  42. package/dist/javascript/node-resolution-result.js +37 -2
  43. package/dist/javascript/node-resolution-result.js.map +1 -1
  44. package/dist/javascript/package-exported-types.d.ts.map +1 -1
  45. package/dist/javascript/package-exported-types.js +56 -72
  46. package/dist/javascript/package-exported-types.js.map +1 -1
  47. package/dist/javascript/parser-utils.d.ts.map +1 -1
  48. package/dist/javascript/parser-utils.js +2 -0
  49. package/dist/javascript/parser-utils.js.map +1 -1
  50. package/dist/javascript/parser.d.ts +7 -1
  51. package/dist/javascript/parser.d.ts.map +1 -1
  52. package/dist/javascript/parser.js +17 -4
  53. package/dist/javascript/parser.js.map +1 -1
  54. package/dist/javascript/recipes/change-import.d.ts +2 -4
  55. package/dist/javascript/recipes/change-import.d.ts.map +1 -1
  56. package/dist/javascript/recipes/change-import.js +8 -244
  57. package/dist/javascript/recipes/change-import.js.map +1 -1
  58. package/dist/javascript/remove-import.d.ts +2 -28
  59. package/dist/javascript/remove-import.d.ts.map +1 -1
  60. package/dist/javascript/remove-import.js +19 -57
  61. package/dist/javascript/remove-import.js.map +1 -1
  62. package/dist/javascript/scope.d.ts +49 -0
  63. package/dist/javascript/scope.d.ts.map +1 -0
  64. package/dist/javascript/scope.js +324 -0
  65. package/dist/javascript/scope.js.map +1 -0
  66. package/dist/javascript/style.d.ts.map +1 -1
  67. package/dist/javascript/style.js +3 -10
  68. package/dist/javascript/style.js.map +1 -1
  69. package/dist/javascript/templating/bindings.d.ts +8 -0
  70. package/dist/javascript/templating/bindings.d.ts.map +1 -0
  71. package/dist/javascript/templating/bindings.js +92 -0
  72. package/dist/javascript/templating/bindings.js.map +1 -0
  73. package/dist/javascript/templating/comparator.d.ts +10 -0
  74. package/dist/javascript/templating/comparator.d.ts.map +1 -1
  75. package/dist/javascript/templating/comparator.js +48 -20
  76. package/dist/javascript/templating/comparator.js.map +1 -1
  77. package/dist/javascript/templating/engine.d.ts +34 -10
  78. package/dist/javascript/templating/engine.d.ts.map +1 -1
  79. package/dist/javascript/templating/engine.js +109 -55
  80. package/dist/javascript/templating/engine.js.map +1 -1
  81. package/dist/javascript/templating/index.d.ts +1 -1
  82. package/dist/javascript/templating/index.d.ts.map +1 -1
  83. package/dist/javascript/templating/index.js.map +1 -1
  84. package/dist/javascript/templating/pattern.d.ts.map +1 -1
  85. package/dist/javascript/templating/pattern.js +13 -10
  86. package/dist/javascript/templating/pattern.js.map +1 -1
  87. package/dist/javascript/templating/placeholder-replacement.d.ts +4 -0
  88. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
  89. package/dist/javascript/templating/placeholder-replacement.js +24 -10
  90. package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
  91. package/dist/javascript/templating/precedence.d.ts +12 -4
  92. package/dist/javascript/templating/precedence.d.ts.map +1 -1
  93. package/dist/javascript/templating/precedence.js +18 -11
  94. package/dist/javascript/templating/precedence.js.map +1 -1
  95. package/dist/javascript/templating/rewrite.d.ts.map +1 -1
  96. package/dist/javascript/templating/rewrite.js +19 -17
  97. package/dist/javascript/templating/rewrite.js.map +1 -1
  98. package/dist/javascript/templating/template.d.ts +14 -0
  99. package/dist/javascript/templating/template.d.ts.map +1 -1
  100. package/dist/javascript/templating/template.js +63 -3
  101. package/dist/javascript/templating/template.js.map +1 -1
  102. package/dist/javascript/templating/types.d.ts +39 -1
  103. package/dist/javascript/templating/types.d.ts.map +1 -1
  104. package/dist/javascript/templating/utils.d.ts +6 -1
  105. package/dist/javascript/templating/utils.d.ts.map +1 -1
  106. package/dist/javascript/templating/utils.js +20 -2
  107. package/dist/javascript/templating/utils.js.map +1 -1
  108. package/dist/javascript/type-mapping.d.ts +1 -0
  109. package/dist/javascript/type-mapping.d.ts.map +1 -1
  110. package/dist/javascript/type-mapping.js +52 -2
  111. package/dist/javascript/type-mapping.js.map +1 -1
  112. package/dist/rewrite-javascript-version.txt +1 -1
  113. package/dist/style.d.ts +2 -0
  114. package/dist/style.d.ts.map +1 -1
  115. package/dist/style.js +5 -0
  116. package/dist/style.js.map +1 -1
  117. package/dist/test/rewrite-test.d.ts +12 -0
  118. package/dist/test/rewrite-test.d.ts.map +1 -1
  119. package/dist/test/rewrite-test.js +40 -3
  120. package/dist/test/rewrite-test.js.map +1 -1
  121. package/package.json +2 -2
  122. package/src/java/formatting-utils.ts +14 -10
  123. package/src/java/type.ts +5 -0
  124. package/src/javascript/add-import.ts +751 -247
  125. package/src/javascript/amd.ts +915 -0
  126. package/src/javascript/assertions.ts +33 -5
  127. package/src/javascript/binding.ts +449 -0
  128. package/src/javascript/comparator.ts +39 -10
  129. package/src/javascript/format/format.ts +9 -10
  130. package/src/javascript/format/tabs-and-indents-visitor.ts +18 -12
  131. package/src/javascript/index.ts +12 -1
  132. package/src/javascript/node-resolution-result.ts +59 -4
  133. package/src/javascript/package-exported-types.ts +60 -74
  134. package/src/javascript/parser-utils.ts +2 -0
  135. package/src/javascript/parser.ts +23 -3
  136. package/src/javascript/recipes/change-import.ts +10 -263
  137. package/src/javascript/remove-import.ts +20 -56
  138. package/src/javascript/scope.ts +338 -0
  139. package/src/javascript/style.ts +3 -11
  140. package/src/javascript/templating/bindings.ts +99 -0
  141. package/src/javascript/templating/comparator.ts +57 -20
  142. package/src/javascript/templating/engine.ts +148 -68
  143. package/src/javascript/templating/index.ts +1 -0
  144. package/src/javascript/templating/pattern.ts +19 -16
  145. package/src/javascript/templating/placeholder-replacement.ts +32 -11
  146. package/src/javascript/templating/precedence.ts +21 -13
  147. package/src/javascript/templating/rewrite.ts +23 -17
  148. package/src/javascript/templating/template.ts +84 -9
  149. package/src/javascript/templating/types.ts +46 -1
  150. package/src/javascript/templating/utils.ts +21 -2
  151. package/src/javascript/type-mapping.ts +54 -2
  152. package/src/style.ts +5 -0
  153. package/src/test/rewrite-test.ts +61 -3
@@ -22,7 +22,6 @@ import {
22
22
  lastWhitespace,
23
23
  normalizeSpaceIndent,
24
24
  replaceIndentAfterLastNewline,
25
- replaceLastWhitespace,
26
25
  spaceContainsNewline,
27
26
  stripLeadingIndent
28
27
  } from "../../java";
@@ -45,6 +44,11 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
45
44
  this.useTabCharacter = this.tabsAndIndentsStyle.useTabCharacter;
46
45
  }
47
46
 
47
+ /** Width of existing indent whitespace in the unit {@link indentString} takes, where a tab is one indent. */
48
+ private indentWidth(indent: string): number {
49
+ return [...indent].reduce((width, ch) => width + (ch === "\t" ? this.indentSize : 1), 0);
50
+ }
51
+
48
52
  private indentString(indent: number): string {
49
53
  if (this.useTabCharacter) {
50
54
  return "\t".repeat(Math.floor(indent / this.indentSize));
@@ -395,12 +399,12 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
395
399
  }
396
400
 
397
401
  private normalizeBlockEnd(block: J.Block, myIndent: number): J.Block {
398
- const effectiveLastWs = lastWhitespace(block.end);
399
- if (!effectiveLastWs.includes("\n")) {
402
+ if (!spaceContainsNewline(block.end)) {
400
403
  return block;
401
404
  }
402
- return produce(block, draft => {
403
- draft.end = replaceLastWhitespace(draft.end, ws => replaceIndentAfterLastNewline(ws, this.indentString(myIndent)));
405
+ const end = normalizeSpaceIndent(block.end, this.indentString(myIndent), this.indentString(myIndent + this.indentSize));
406
+ return end === block.end ? block : produce(block, draft => {
407
+ draft.end = end;
404
408
  });
405
409
  }
406
410
 
@@ -440,12 +444,14 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
440
444
  // Normalize the last element's after whitespace (closing delimiter like `)`)
441
445
  // The closing delimiter should align with the parent's indent level
442
446
  if (container.elements.length > 0) {
443
- const effectiveLastWs = lastWhitespace(container.elements[container.elements.length - 1].after);
444
- if (effectiveLastWs.includes("\n")) {
445
- return produce(container, draft => {
446
- const lastDraft = draft.elements[draft.elements.length - 1];
447
- lastDraft.after = replaceLastWhitespace(lastDraft.after, ws => replaceIndentAfterLastNewline(ws, this.indentString(parentIndent)));
448
- });
447
+ const last = container.elements[container.elements.length - 1];
448
+ if (spaceContainsNewline(last.after)) {
449
+ const after = normalizeSpaceIndent(last.after, this.indentString(parentIndent), this.indentString(parentIndent + this.indentSize));
450
+ if (after !== last.after) {
451
+ return produce(container, draft => {
452
+ draft.elements[draft.elements.length - 1].after = after;
453
+ });
454
+ }
449
455
  }
450
456
  }
451
457
 
@@ -646,7 +652,7 @@ export class TabsAndIndentsVisitor<P> extends JavaScriptVisitor<P> {
646
652
  const idx = ws.lastIndexOf('\n');
647
653
  if (idx !== -1) {
648
654
  anchorCursor = c;
649
- anchorIndent = ws.length - idx - 1;
655
+ anchorIndent = this.indentWidth(ws.substring(idx + 1));
650
656
  }
651
657
  }
652
658
 
@@ -31,7 +31,18 @@ export * from "./autodetect";
31
31
  export * from "./tree-debug";
32
32
  export * from "./project-parser";
33
33
 
34
- export * from "./add-import";
34
+ export type {Scope} from "./scope";
35
+ export {scopeOf, namesDeclaredIn, bindingNames} from "./scope";
36
+ export type {QuoteChar, AddImportOptions} from "./add-import";
37
+ export {ImportStyle, moduleNameOf, AddImport} from "./add-import";
38
+ // AMD mechanics `recipes-ui5` builds on directly, beyond the `maybeBind` surface below.
39
+ export type {AmdBlock} from "./amd";
40
+ export {
41
+ DEFAULT_AMD_CALLEES, amdBlockOf, dependencyNames, parameterNames, withDependency, withoutDependencyAt,
42
+ RemoveAmdDependency, removeNewlyUnusedAmdBindings
43
+ } from "./amd";
44
+ export type {MaybeBindOptions, MaybeUnbindOptions, MaybeRebindOptions, ModuleBindings} from "./binding";
45
+ export {maybeBind, maybeUnbind, maybeRebind, moduleBindings, isAmdBlock, maybeRemoveImport, maybeAddImport} from "./binding";
35
46
  export * from "./remove-import";
36
47
  export * from "./cleanup/index";
37
48
  export * from "./recipes/index";
@@ -60,13 +60,65 @@ export interface PackageLockEntry {
60
60
  }
61
61
 
62
62
  /**
63
- * Parsed package-lock.json content structure (npm lockfile v3 format).
63
+ * Entry in a legacy npm lockfileVersion 1 `dependencies` tree (npm 5/6), with its
64
+ * requirements under `requires` and nested conflicting versions under `dependencies`.
65
+ */
66
+ export interface LockfileV1Entry {
67
+ readonly version?: string;
68
+ readonly resolved?: string;
69
+ readonly integrity?: string;
70
+ readonly license?: string | string[] | { type?: string; url?: string };
71
+ readonly engines?: Record<string, string> | string[];
72
+ readonly requires?: Record<string, string>;
73
+ readonly dependencies?: Record<string, LockfileV1Entry>;
74
+ }
75
+
76
+ /**
77
+ * Parsed package-lock.json content structure. The `packages` map is the modern
78
+ * (lockfileVersion 2/3) shape; `dependencies` is the legacy lockfileVersion 1 tree.
64
79
  */
65
80
  export interface PackageLockContent {
66
81
  readonly name?: string;
67
82
  readonly version?: string;
68
83
  readonly lockfileVersion?: number;
69
84
  readonly packages?: Record<string, PackageLockEntry>;
85
+ readonly dependencies?: Record<string, LockfileV1Entry>;
86
+ }
87
+
88
+ /**
89
+ * Converts a legacy npm lockfileVersion 1 `dependencies` tree into the modern
90
+ * `packages` map keyed by node_modules path, so the resolver can walk transitive
91
+ * dependencies the same way it does for lockfileVersion 2/3 files. Returns
92
+ * undefined when the tree is absent or empty.
93
+ */
94
+ function convertV1DependencyTree(
95
+ tree: Record<string, LockfileV1Entry> | undefined
96
+ ): Record<string, PackageLockEntry> | undefined {
97
+ if (!tree || Object.keys(tree).length === 0) {
98
+ return undefined;
99
+ }
100
+ const packages: Record<string, PackageLockEntry> = {};
101
+
102
+ function walk(deps: Record<string, LockfileV1Entry>, pathPrefix: string): void {
103
+ for (const [name, entry] of Object.entries(deps)) {
104
+ const pkgPath = `${pathPrefix}node_modules/${name}`;
105
+ packages[pkgPath] = {
106
+ version: entry.version,
107
+ resolved: entry.resolved,
108
+ integrity: entry.integrity,
109
+ license: entry.license,
110
+ engines: entry.engines,
111
+ // v1 folds all declared deps into `requires`; the resolver reads `dependencies`.
112
+ dependencies: entry.requires,
113
+ };
114
+ if (entry.dependencies) {
115
+ walk(entry.dependencies, `${pkgPath}/`);
116
+ }
117
+ }
118
+ }
119
+
120
+ walk(tree, "");
121
+ return packages;
70
122
  }
71
123
 
72
124
  /**
@@ -524,9 +576,12 @@ export function createNodeResolutionResultMarker(
524
576
  function parseResolutions(
525
577
  lockContent: PackageLockContent
526
578
  ): ResolvedDependency[] {
527
- if (!lockContent.packages) return [];
528
-
529
- const packages = lockContent.packages;
579
+ // Prefer the modern `packages` map; fall back to converting a legacy
580
+ // lockfileVersion 1 `dependencies` tree when no `packages` map is present.
581
+ const packages = lockContent.packages && Object.keys(lockContent.packages).length > 0
582
+ ? lockContent.packages
583
+ : convertV1DependencyTree(lockContent.dependencies);
584
+ if (!packages) return [];
530
585
 
531
586
  // First pass: Create all ResolvedDependency placeholders and build path map
532
587
  const packageInfos: Array<{ path: string; name: string; version: string; entry: PackageLockEntry }> = [];
@@ -18,6 +18,8 @@ import * as path from "path";
18
18
  import {Type} from "../java";
19
19
  import {JavaScriptTypeMapping} from "./type-mapping";
20
20
 
21
+ const TYPESCRIPT_FILE = /\.[cm]?tsx?$/;
22
+
21
23
  // Free functions are not exported-type entries; only nominal types (and value modules, which are
22
24
  // descended into for nested types) qualify.
23
25
  const TYPE_SYMBOL_FLAGS =
@@ -38,21 +40,13 @@ const TYPE_SYMBOL_FLAGS =
38
40
  * @param references node_modules roots / sibling packages used only for symbol resolution
39
41
  */
40
42
  export function exportedTypes(ownArtifacts: string[], references: string[]): Type.FullyQualified[] {
41
- const entries: string[] = [];
42
- for (const artifact of ownArtifacts) {
43
- const entry = declarationFileEntry(artifact) ?? resolvePackageEntry(artifact);
44
- if (entry) {
45
- entries.push(path.normalize(entry));
46
- }
47
- }
48
- if (entries.length === 0) {
49
- return [];
50
- }
51
-
52
43
  const compilerOptions: ts.CompilerOptions = {
53
44
  target: ts.ScriptTarget.Latest,
54
- module: ts.ModuleKind.CommonJS,
55
- moduleResolution: ts.ModuleResolutionKind.Node10,
45
+ // Mirrors JavaScriptParser's module resolution, so a dependency's own imports resolve here
46
+ // as they do when its consumers are parsed; the rationale for each option lives there.
47
+ module: ts.ModuleKind.Preserve,
48
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
49
+ customConditions: ["node"],
56
50
  noEmit: true,
57
51
  allowJs: true,
58
52
  checkJs: false,
@@ -80,6 +74,17 @@ export function exportedTypes(ownArtifacts: string[], references: string[]): Typ
80
74
  return resolved;
81
75
  });
82
76
 
77
+ const entries: string[] = [];
78
+ for (const artifact of ownArtifacts) {
79
+ const entry = declarationFileEntry(artifact) ?? resolvePackageEntry(artifact, compilerOptions, host);
80
+ if (entry) {
81
+ entries.push(path.normalize(entry));
82
+ }
83
+ }
84
+ if (entries.length === 0) {
85
+ return [];
86
+ }
87
+
83
88
  const program = ts.createProgram(entries, compilerOptions, host);
84
89
  const checker = program.getTypeChecker();
85
90
  const typeMapping = new JavaScriptTypeMapping(checker);
@@ -156,7 +161,7 @@ function arity(cls: Type.Class): number {
156
161
 
157
162
  /** A TypeScript declaration/source FILE (e.g. a `lib.*.d.ts` of ambient globals), used directly as a program entry. */
158
163
  function declarationFileEntry(artifact: string): string | undefined {
159
- return ts.sys.fileExists(artifact) && /\.[cm]?tsx?$/.test(artifact) ? artifact : undefined;
164
+ return ts.sys.fileExists(artifact) && TYPESCRIPT_FILE.test(artifact) ? artifact : undefined;
160
165
  }
161
166
 
162
167
  /** Top-level ambient type declarations of a script (non-module) file — a `lib.*.d.ts`'s globals; namespaces descended for nested types. */
@@ -186,30 +191,34 @@ function globalTypeSymbols(checker: ts.TypeChecker, sourceFile: ts.SourceFile):
186
191
  }
187
192
 
188
193
  /**
189
- * Resolve a package directory to its declaration entry: the package.json `types`/`typings`
190
- * field, else the `exports["."]` types condition (modern packages declare types only there),
191
- * else a top-level `index.d.ts` (then `index.ts`).
194
+ * A package directory's declaration entry, found by resolving the package's own name from inside
195
+ * it so `types`, `exports` conditions and `typesVersions` all apply; else the declared `types`
196
+ * file; else a top-level `index.d.ts` (then `index.ts`). Reaching none of them means the package
197
+ * ships no types, which leaves the caller to fall back to its DefinitelyTyped package.
192
198
  */
193
- function resolvePackageEntry(dir: string): string | undefined {
194
- const packageJson = path.join(dir, "package.json");
195
- if (ts.sys.fileExists(packageJson)) {
196
- try {
197
- const pkg = JSON.parse(ts.sys.readFile(packageJson) || "{}");
198
- const typesField: unknown = pkg.types ?? pkg.typings;
199
- const candidate = typeof typesField === "string"
200
- ? typesField
201
- : exportsTypesEntry(pkg.exports);
202
- if (typeof candidate === "string") {
203
- const resolved = path.resolve(dir, candidate);
204
- if (ts.sys.fileExists(resolved)) {
205
- return resolved;
206
- }
207
- if (ts.sys.fileExists(resolved + ".d.ts")) {
208
- return resolved + ".d.ts";
209
- }
199
+ function resolvePackageEntry(dir: string, options: ts.CompilerOptions, host: ts.ModuleResolutionHost): string | undefined {
200
+ const manifest = packageManifest(dir);
201
+ if (typeof manifest?.name === "string") {
202
+ const conditions = options.customConditions ?? [];
203
+ // A package's declarations count under whichever condition carries them, since no
204
+ // consumer's import mode is choosing between `import` and `require` here.
205
+ for (const attempt of [conditions, [...conditions, "require"]]) {
206
+ const resolved = ts.resolveModuleName(manifest.name, path.join(dir, "__entry__.ts"),
207
+ {...options, customConditions: attempt}, host).resolvedModule?.resolvedFileName;
208
+ if (resolved && TYPESCRIPT_FILE.test(resolved) && containedIn(dir, resolved)) {
209
+ return resolved;
210
+ }
211
+ }
212
+ }
213
+ // An `exports` map without a `types` condition takes resolution to the runtime entry and stops
214
+ // it there, while `types` still names the declarations.
215
+ const declared = manifest?.types ?? manifest?.typings;
216
+ if (typeof declared === "string") {
217
+ const resolved = path.resolve(dir, declared);
218
+ for (const candidate of [resolved, resolved + ".d.ts"]) {
219
+ if (ts.sys.fileExists(candidate)) {
220
+ return candidate;
210
221
  }
211
- } catch {
212
- // Malformed package.json; fall through to conventional entry points.
213
222
  }
214
223
  }
215
224
  for (const candidate of ["index.d.ts", "index.ts", "index.tsx"]) {
@@ -221,49 +230,26 @@ function resolvePackageEntry(dir: string): string | undefined {
221
230
  return undefined;
222
231
  }
223
232
 
224
- /** The declaration file for the package's `.` subpath from its `exports` map, if any. */
225
- function exportsTypesEntry(exports: unknown): string | undefined {
226
- if (typeof exports === "string") {
227
- return typesFromCondition(exports);
228
- }
229
- if (!exports || typeof exports !== "object") {
230
- return undefined;
231
- }
232
- const map = exports as Record<string, unknown>;
233
- // `exports["."]`, or the whole object when it is condition-only sugar (no subpath keys).
234
- const dot = "." in map
235
- ? map["."]
236
- : (Object.keys(map).some(k => k.startsWith(".")) ? undefined : map);
237
- return dot === undefined ? undefined : typesFromCondition(dot);
238
- }
239
-
240
233
  /**
241
- * Pull a declaration file out of an `exports` condition value: an explicit `types` condition,
242
- * else the `types` nested under `import`/`require`/`default`. A bare target string is a runtime
243
- * (JS) entry, not types, unless it is itself a declaration file.
234
+ * Whether `file` belongs to the package installed at `dir`. Resolution by name walks up the tree,
235
+ * so a same-named package elsewhere can answer which an npm alias (`"a": "npm:b@1"`) installs by
236
+ * construction.
244
237
  */
245
- function typesFromCondition(entry: unknown): string | undefined {
246
- if (typeof entry === "string") {
247
- return /\.d\.[cm]?ts$/.test(entry) ? entry : undefined;
248
- }
249
- if (!entry || typeof entry !== "object") {
238
+ function containedIn(dir: string, file: string): boolean {
239
+ return path.resolve(file).startsWith(path.resolve(dir) + path.sep);
240
+ }
241
+
242
+ function packageManifest(dir: string): {name?: unknown, types?: unknown, typings?: unknown} | undefined {
243
+ const packageJson = path.join(dir, "package.json");
244
+ if (!ts.sys.fileExists(packageJson)) {
250
245
  return undefined;
251
246
  }
252
- const o = entry as Record<string, unknown>;
253
- if (typeof o.types === "string") {
254
- return o.types;
255
- }
256
- const nestedTypes = typesFromCondition(o.types);
257
- if (nestedTypes) {
258
- return nestedTypes;
259
- }
260
- for (const cond of ["import", "require", "default"]) {
261
- const nested = typesFromCondition(o[cond]);
262
- if (nested) {
263
- return nested;
264
- }
247
+ try {
248
+ return JSON.parse(ts.sys.readFile(packageJson) || "{}");
249
+ } catch {
250
+ // Malformed package.json; fall through to conventional entry points.
251
+ return undefined;
265
252
  }
266
- return undefined;
267
253
  }
268
254
 
269
255
  /**
@@ -394,6 +394,7 @@ const excludedCodes = new Set([
394
394
  1183, // An implementation cannot be declared in ambient contexts.
395
395
  1203, // Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.
396
396
  1207, // Decorators cannot be applied to multiple get/set accessors of the same name.
397
+ 1212, // Identifier expected. '{0}' is a reserved word in strict mode. The node is still parsed as an identifier.
397
398
  1215, // Invalid use of '{0}'. Modules are automatically in strict mode.
398
399
  1238, // Unable to resolve signature of class decorator when called as an expression.
399
400
  1239, // Unable to resolve signature of parameter decorator when called as an expression.
@@ -422,6 +423,7 @@ const excludedCodes = new Set([
422
423
  1375, // 'await' expressions are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.
423
424
  1378, // Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
424
425
  1432, // Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.
426
+ 1540, // A 'namespace' declaration should not be declared using the 'module' keyword. The `module` spelling yields a ModuleDeclaration.
425
427
  ]);
426
428
 
427
429
  // Errors to exclude only for JavaScript files (.js, .jsx, .mjs, .cjs)
@@ -54,6 +54,12 @@ import SpreadAttribute = JSX.SpreadAttribute;
54
54
  export interface JavaScriptParserOptions extends ParserOptions {
55
55
  styles?: NamedStyles[],
56
56
  sourceFileCache?: Map<string, ts.SourceFile>,
57
+ /**
58
+ * Type packages to load whose declarations nothing imports. Unset leaves TypeScript's default,
59
+ * which reads `@types/*` and nothing else; a package declaring its modules ambiently needs
60
+ * naming here for those declarations to be in scope.
61
+ */
62
+ types?: string[],
57
63
  }
58
64
 
59
65
  function getScriptKindFromFileName(fileName: string): ts.ScriptKind {
@@ -86,13 +92,20 @@ export class JavaScriptParser extends Parser {
86
92
  relativeTo,
87
93
  styles,
88
94
  sourceFileCache,
95
+ types,
89
96
  }: JavaScriptParserOptions = {},
90
97
  ) {
91
98
  super({ctx, relativeTo});
92
99
  this.compilerOptions = {
93
100
  target: ts.ScriptTarget.Latest,
94
- module: ts.ModuleKind.CommonJS,
95
- moduleResolution: ts.ModuleResolutionKind.Node10,
101
+ // Bundler matches `exports` conditions leniently, so packages that publish types only under
102
+ // an `import` condition resolve; it pairs with `preserve`, which `commonjs` cannot (TS5095).
103
+ // A `node16` module kind makes TS1479 reachable, and that error costs the whole LST.
104
+ module: ts.ModuleKind.Preserve,
105
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
106
+ // Bundler's own condition set omits `node`, which takes the browser branch of packages
107
+ // that publish one, typing them against an API surface the source does not use.
108
+ customConditions: ["node"],
96
109
  noEmit: true,
97
110
  allowJs: true,
98
111
  checkJs: true,
@@ -102,6 +115,12 @@ export class JavaScriptParser extends Parser {
102
115
  emitDecoratorMetadata: true,
103
116
  forceConsistentCasingInFileNames: false,
104
117
  jsx: ts.JsxEmit.Preserve,
118
+ // Strictness is a property of the parsed project rather than of the compiler we pin: under
119
+ // `strictNullChecks` every nullable type carries an extra `null` union constituent.
120
+ strict: false,
121
+ // `types` defaults to `[]`, which attributes ambient `@types/*` packages only when named;
122
+ // `*` enumerates everything under `node_modules/@types`.
123
+ types: types ?? ["*"],
105
124
  baseUrl: relativeTo || process.cwd()
106
125
  };
107
126
  this.styles = styles;
@@ -326,7 +345,8 @@ export class JavaScriptParser extends Parser {
326
345
  return resolvedModules;
327
346
  };
328
347
 
329
- // Create a new Program, passing the oldProgram for incremental parsing
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.
330
350
  const program = ts.createProgram([...inputFiles.keys()], this.compilerOptions, host, this.oldProgram);
331
351
 
332
352
  // Update the oldProgram reference