Fix #41800 correctly. (#41895)

* fix as suggestion.

* Update moduleSpecifiers.ts

* compare symbol rather than string

* fix typo.

* fix

* fix lint.

* better name and more clear code

* fix comment.

Co-authored-by: Orta Therox <git@orta.io>
This commit is contained in:
Song Gao
2020-12-17 10:56:18 +08:00
committed by GitHub
parent b217f22e79
commit 8cbc576954
8 changed files with 262 additions and 39 deletions

View File

@@ -5494,6 +5494,7 @@ namespace ts {
const specifierCompilerOptions = isBundle ? { ...compilerOptions, baseUrl: moduleResolverHost.getCommonSourceDirectory() } : compilerOptions;
specifier = first(moduleSpecifiers.getModuleSpecifiers(
symbol,
checker,
specifierCompilerOptions,
contextFile,
moduleResolverHost,

View File

@@ -1422,8 +1422,7 @@ namespace ts {
export function getPackageNameFromTypesPackageName(mangledName: string): string {
const withoutAtTypePrefix = removePrefix(mangledName, "@types/");
if (withoutAtTypePrefix !== mangledName) {
const withoutAtTypeNodePrefix = removePrefix(withoutAtTypePrefix, "node/");
return unmangleScopedPackageName(withoutAtTypeNodePrefix);
return unmangleScopedPackageName(withoutAtTypePrefix);
}
return mangledName;
}

View File

@@ -92,12 +92,13 @@ namespace ts.moduleSpecifiers {
/** Returns an import for each symlink and for the realpath. */
export function getModuleSpecifiers(
moduleSymbol: Symbol,
checker: TypeChecker,
compilerOptions: CompilerOptions,
importingSourceFile: SourceFile,
host: ModuleSpecifierResolutionHost,
userPreferences: UserPreferences,
): readonly string[] {
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol);
const ambient = tryGetModuleNameFromAmbientModule(moduleSymbol, checker);
if (ambient) return [ambient];
const info = getInfo(importingSourceFile.path, host);
@@ -369,13 +370,49 @@ namespace ts.moduleSpecifiers {
return sortedPaths;
}
function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined {
function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol, checker: TypeChecker): string | undefined {
const decl = find(moduleSymbol.declarations,
d => isNonGlobalAmbientModule(d) && (!isExternalModuleAugmentation(d) || !isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(d.name)))
) as (ModuleDeclaration & { name: StringLiteral }) | undefined;
if (decl) {
return decl.name.text;
}
// the module could be a namespace, which is export through "export=" from an ambient module.
/**
* declare module "m" {
* namespace ns {
* class c {}
* }
* export = ns;
* }
*/
// `import {c} from "m";` is valid, in which case, `moduleSymbol` is "ns", but the module name should be "m"
const ambientModuleDeclareCandidates = mapDefined(moduleSymbol.declarations,
d => {
if (!isModuleDeclaration(d)) return;
const topNamespace = getTopNamespace(d);
if (!(topNamespace?.parent?.parent
&& isModuleBlock(topNamespace.parent) && isAmbientModule(topNamespace.parent.parent) && isSourceFile(topNamespace.parent.parent.parent))) return;
const exportAssignment = ((topNamespace.parent.parent.symbol.exports?.get("export=" as __String)?.valueDeclaration as ExportAssignment)?.expression as PropertyAccessExpression | Identifier);
if (!exportAssignment) return;
const exportSymbol = checker.getSymbolAtLocation(exportAssignment);
if (!exportSymbol) return;
const originalExportSymbol = exportSymbol?.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(exportSymbol) : exportSymbol;
if (originalExportSymbol === d.symbol) return topNamespace.parent.parent;
function getTopNamespace(namespaceDeclaration: ModuleDeclaration) {
while (namespaceDeclaration.flags & NodeFlags.NestedNamespace) {
namespaceDeclaration = namespaceDeclaration.parent as ModuleDeclaration;
}
return namespaceDeclaration;
}
}
);
const ambientModuleDeclare = ambientModuleDeclareCandidates[0] as (AmbientModuleDeclaration & { name: StringLiteral }) | undefined;
if (ambientModuleDeclare) {
return ambientModuleDeclare.name.text;
}
}
function tryGetModuleNameFromPaths(relativeToBaseUrlWithIndex: string, relativeToBaseUrl: string, paths: MapLike<readonly string[]>): string | undefined {

View File

@@ -404,7 +404,7 @@ namespace ts.codefix {
const { allowsImportingSpecifier } = createAutoImportFilter(sourceFile, program, host);
const choicesForEachExportingModule = flatMap(moduleSymbols, ({ moduleSymbol, importKind, exportedSymbolIsTypeOnly }) =>
moduleSpecifiers.getModuleSpecifiers(moduleSymbol, compilerOptions, sourceFile, createModuleSpecifierResolutionHost(program, host) , preferences)
moduleSpecifiers.getModuleSpecifiers(moduleSymbol, program.getTypeChecker(), compilerOptions, sourceFile, createModuleSpecifierResolutionHost(program, host), preferences)
.map((moduleSpecifier): FixAddNewImport | FixUseImportType =>
// `position` should only be undefined at a missing jsx namespace, in which case we shouldn't be looking for pure types.
exportedSymbolIsTypeOnly && isJs

View File

@@ -11,30 +11,73 @@ declare module "events" {
}
export = EventEmitter;
}
declare module "nestNamespaceModule" {
namespace a1.a2 {
class d { }
}
namespace a1.a2.n3 {
class c { }
}
export = a1.a2;
}
declare module "renameModule" {
namespace a.b {
class c { }
}
import d = a.b;
export = d;
}
//// [b.js]
import { EventEmitter } from 'events';
class Foo extends EventEmitter {
constructor() {
super();
}
import { n3, d } from 'nestNamespaceModule';
import { c } from 'renameModule';
export class Foo extends EventEmitter {
}
export default Foo;
export class Foo2 extends n3.c {
}
export class Foo3 extends d {
}
export class Foo4 extends c {
}
//// [b.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Foo4 = exports.Foo3 = exports.Foo2 = exports.Foo = void 0;
const events_1 = require("events");
const nestNamespaceModule_1 = require("nestNamespaceModule");
const renameModule_1 = require("renameModule");
class Foo extends events_1.EventEmitter {
constructor() {
super();
}
}
exports.default = Foo;
exports.Foo = Foo;
class Foo2 extends nestNamespaceModule_1.n3.c {
}
exports.Foo2 = Foo2;
class Foo3 extends nestNamespaceModule_1.d {
}
exports.Foo3 = Foo3;
class Foo4 extends renameModule_1.c {
}
exports.Foo4 = Foo4;
//// [b.d.ts]
export default Foo;
declare class Foo extends EventEmitter {
export class Foo extends EventEmitter {
}
export class Foo2 extends n3.c {
}
export class Foo3 extends d {
}
export class Foo4 extends c {
}
import { EventEmitter } from "events";
import { n3 } from "nestNamespaceModule";
import { d } from "nestNamespaceModule";
import { c } from "renameModule";

View File

@@ -16,20 +16,79 @@ declare module "events" {
export = EventEmitter;
>EventEmitter : Symbol(EventEmitter, Decl(events.d.ts, 0, 25))
}
declare module "nestNamespaceModule" {
>"nestNamespaceModule" : Symbol("nestNamespaceModule", Decl(events.d.ts, 7, 1))
namespace a1.a2 {
>a1 : Symbol(a1, Decl(events.d.ts, 8, 38), Decl(events.d.ts, 11, 5))
>a2 : Symbol(a2, Decl(events.d.ts, 9, 17), Decl(events.d.ts, 13, 17))
class d { }
>d : Symbol(d, Decl(events.d.ts, 9, 21))
}
namespace a1.a2.n3 {
>a1 : Symbol(a1, Decl(events.d.ts, 8, 38), Decl(events.d.ts, 11, 5))
>a2 : Symbol(a2, Decl(events.d.ts, 9, 17), Decl(events.d.ts, 13, 17))
>n3 : Symbol(n3, Decl(events.d.ts, 13, 20))
class c { }
>c : Symbol(c, Decl(events.d.ts, 13, 24))
}
export = a1.a2;
>a1.a2 : Symbol(a1.a2, Decl(events.d.ts, 9, 17), Decl(events.d.ts, 13, 17))
>a1 : Symbol(a1, Decl(events.d.ts, 8, 38), Decl(events.d.ts, 11, 5))
>a2 : Symbol(a1.a2, Decl(events.d.ts, 9, 17), Decl(events.d.ts, 13, 17))
}
declare module "renameModule" {
>"renameModule" : Symbol("renameModule", Decl(events.d.ts, 17, 1))
namespace a.b {
>a : Symbol(a, Decl(events.d.ts, 18, 31))
>b : Symbol(b, Decl(events.d.ts, 19, 16))
class c { }
>c : Symbol(c, Decl(events.d.ts, 19, 19))
}
import d = a.b;
>d : Symbol(d, Decl(events.d.ts, 21, 5))
>a : Symbol(a, Decl(events.d.ts, 18, 31))
>b : Symbol(d, Decl(events.d.ts, 19, 16))
export = d;
>d : Symbol(d, Decl(events.d.ts, 21, 5))
}
=== /src/b.js ===
import { EventEmitter } from 'events';
>EventEmitter : Symbol(EventEmitter, Decl(b.js, 0, 8))
class Foo extends EventEmitter {
>Foo : Symbol(Foo, Decl(b.js, 0, 38))
import { n3, d } from 'nestNamespaceModule';
>n3 : Symbol(n3, Decl(b.js, 1, 8))
>d : Symbol(d, Decl(b.js, 1, 12))
import { c } from 'renameModule';
>c : Symbol(c, Decl(b.js, 2, 8))
export class Foo extends EventEmitter {
>Foo : Symbol(Foo, Decl(b.js, 2, 33))
>EventEmitter : Symbol(EventEmitter, Decl(b.js, 0, 8))
constructor() {
super();
>super : Symbol(EventEmitter, Decl(events.d.ts, 1, 28))
}
}
export default Foo;
>Foo : Symbol(Foo, Decl(b.js, 0, 38))
export class Foo2 extends n3.c {
>Foo2 : Symbol(Foo2, Decl(b.js, 5, 1))
>n3.c : Symbol(n3.c, Decl(events.d.ts, 13, 24))
>n3 : Symbol(n3, Decl(b.js, 1, 8))
>c : Symbol(n3.c, Decl(events.d.ts, 13, 24))
}
export class Foo3 extends d {
>Foo3 : Symbol(Foo3, Decl(b.js, 8, 1))
>d : Symbol(d, Decl(b.js, 1, 12))
}
export class Foo4 extends c {
>Foo4 : Symbol(Foo4, Decl(b.js, 11, 1))
>c : Symbol(c, Decl(b.js, 2, 8))
}

View File

@@ -16,21 +16,79 @@ declare module "events" {
export = EventEmitter;
>EventEmitter : typeof EventEmitter
}
declare module "nestNamespaceModule" {
>"nestNamespaceModule" : typeof import("nestNamespaceModule")
namespace a1.a2 {
>a1 : typeof a1
>a2 : typeof a2
class d { }
>d : d
}
namespace a1.a2.n3 {
>a1 : typeof a1
>a2 : typeof a2
>n3 : typeof n3
class c { }
>c : c
}
export = a1.a2;
>a1.a2 : typeof a1.a2
>a1 : typeof a1
>a2 : typeof a1.a2
}
declare module "renameModule" {
>"renameModule" : typeof import("renameModule")
namespace a.b {
>a : typeof a
>b : typeof b
class c { }
>c : c
}
import d = a.b;
>d : typeof d
>a : typeof a
>b : typeof d
export = d;
>d : typeof d
}
=== /src/b.js ===
import { EventEmitter } from 'events';
>EventEmitter : typeof EventEmitter
class Foo extends EventEmitter {
import { n3, d } from 'nestNamespaceModule';
>n3 : typeof n3
>d : typeof d
import { c } from 'renameModule';
>c : typeof c
export class Foo extends EventEmitter {
>Foo : Foo
>EventEmitter : EventEmitter
constructor() {
super();
>super() : void
>super : typeof EventEmitter
}
}
export default Foo;
>Foo : Foo
export class Foo2 extends n3.c {
>Foo2 : Foo2
>n3.c : n3.c
>n3 : typeof n3
>c : typeof n3.c
}
export class Foo3 extends d {
>Foo3 : Foo3
>d : d
}
export class Foo4 extends c {
>Foo4 : Foo4
>c : c
}

View File

@@ -15,12 +15,38 @@ declare module "events" {
}
export = EventEmitter;
}
declare module "nestNamespaceModule" {
namespace a1.a2 {
class d { }
}
namespace a1.a2.n3 {
class c { }
}
export = a1.a2;
}
declare module "renameModule" {
namespace a.b {
class c { }
}
import d = a.b;
export = d;
}
// @filename: /src/b.js
import { EventEmitter } from 'events';
class Foo extends EventEmitter {
constructor() {
super();
}
import { n3, d } from 'nestNamespaceModule';
import { c } from 'renameModule';
export class Foo extends EventEmitter {
}
export default Foo;
export class Foo2 extends n3.c {
}
export class Foo3 extends d {
}
export class Foo4 extends c {
}