Skip to content

Commit a77af13

Browse files
committed
Resolve npm package references to the ambient module when not found.
1 parent 58f8bcf commit a77af13

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

src/compiler/checker.ts

+19
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,25 @@ namespace ts {
35953595
}
35963596

35973597
function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node, isForAugmentation = false): Symbol | undefined {
3598+
const result = resolveExternalModuleInner(location, moduleReference, moduleNotFoundError, errorNode, isForAugmentation);
3599+
3600+
// deno: attempt to resolve an npm package reference to its bare specifier w/ path ambient module
3601+
// when not found and the symbol has zero exports
3602+
if (moduleReference.startsWith("npm:") && (result == null || result?.exports?.size === 0)) {
3603+
const npmPackageRef = deno.tryParseNpmPackageReference(moduleReference);
3604+
if (npmPackageRef) {
3605+
const bareSpecifier = npmPackageRef.name + (npmPackageRef.subPath == null ? "" : "/" + npmPackageRef.subPath);
3606+
const ambientModule = tryFindAmbientModule(bareSpecifier, /*withAugmentations*/ true);
3607+
if (ambientModule) {
3608+
return ambientModule;
3609+
}
3610+
}
3611+
}
3612+
3613+
return result;
3614+
}
3615+
3616+
function resolveExternalModuleInner(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage | undefined, errorNode: Node, isForAugmentation = false): Symbol | undefined {
35983617
if (startsWith(moduleReference, "@types/")) {
35993618
const diag = Diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1;
36003619
const withoutAtTypePrefix = removePrefix(moduleReference, "@types/");

src/compiler/deno.ts

+39
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,43 @@ namespace ts.deno {
174174
}
175175
}
176176
}
177+
178+
export interface NpmPackageReference {
179+
name: string;
180+
versionReq: string;
181+
subPath: string | undefined;
182+
}
183+
184+
export function tryParseNpmPackageReference(text: string) {
185+
try {
186+
return parseNpmPackageReference(text);
187+
} catch {
188+
return undefined;
189+
}
190+
}
191+
192+
export function parseNpmPackageReference(text: string) {
193+
if (!text.startsWith("npm:")) {
194+
throw new Error(`Not an npm specifier: ${text}`);
195+
}
196+
text = text.replace(/^npm:/, "");
197+
const parts = text.split("/");
198+
const namePartLen = text.startsWith("@") ? 2 : 1;
199+
if (parts.length < namePartLen) {
200+
throw new Error(`Not a valid package: ${text}`);
201+
}
202+
const nameParts = parts.slice(0, namePartLen);
203+
const lastNamePart = nameParts.at(-1)!;
204+
const lastAtIndex = lastNamePart.lastIndexOf("@");
205+
let versionReq: string | undefined = undefined;
206+
if (lastAtIndex > 0) {
207+
versionReq = lastNamePart.substring(lastAtIndex + 1);
208+
nameParts[nameParts.length - 1] = lastNamePart.substring(0, lastAtIndex);
209+
}
210+
return {
211+
name: nameParts.join("/"),
212+
versionReq,
213+
subPath: parts.length > nameParts.length ? parts.slice(nameParts.length).join("/") : undefined,
214+
};
215+
}
177216
}

0 commit comments

Comments
 (0)