Skip to content

Commit fa7a59f

Browse files
committed
Merge pull request #4744 from Microsoft/Port-4738
Ports PR-4738 into release-1.6
2 parents f2b8e4b + a880be1 commit fa7a59f

28 files changed

+347
-144
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -976,8 +976,8 @@ namespace ts {
976976
}
977977
}
978978

979-
let fileName = getResolvedModuleFileName(getSourceFile(location), moduleReferenceLiteral.text);
980-
let sourceFile = fileName && host.getSourceFile(fileName);
979+
let resolvedModule = getResolvedModule(getSourceFile(location), moduleReferenceLiteral.text);
980+
let sourceFile = resolvedModule && host.getSourceFile(resolvedModule.resolvedFileName);
981981
if (sourceFile) {
982982
if (sourceFile.symbol) {
983983
return sourceFile.symbol;

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ namespace ts {
427427
Cannot_emit_namespaced_JSX_elements_in_React: { code: 2650, category: DiagnosticCategory.Error, key: "Cannot emit namespaced JSX elements in React" },
428428
A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums: { code: 2651, category: DiagnosticCategory.Error, key: "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums." },
429429
Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead: { code: 2652, category: DiagnosticCategory.Error, key: "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead." },
430+
Exported_external_package_typings_file_cannot_contain_tripleslash_references_Please_contact_the_package_author_to_update_the_package_definition: { code: 2654, category: DiagnosticCategory.Error, key: "Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition." },
431+
Exported_external_package_typings_can_only_be_in_d_ts_files_Please_contact_the_package_author_to_update_the_package_definition: { code: 2655, category: DiagnosticCategory.Error, key: "Exported external package typings can only be in '.d.ts' files. Please contact the package author to update the package definition." },
430432
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
431433
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
432434
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,14 @@
16971697
"category": "Error",
16981698
"code": 2652
16991699
},
1700+
"Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition.": {
1701+
"category": "Error",
1702+
"code": 2654
1703+
},
1704+
"Exported external package typings can only be in '.d.ts' files. Please contact the package author to update the package definition.": {
1705+
"category": "Error",
1706+
"code": 2655
1707+
},
17001708
"Import declaration '{0}' is using private name '{1}'.": {
17011709
"category": "Error",
17021710
"code": 4000

src/compiler/program.ts

Lines changed: 44 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace ts {
3636
return normalizePath(referencedFileName);
3737
}
3838

39-
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule {
39+
export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
4040
let moduleResolution = compilerOptions.moduleResolution !== undefined
4141
? compilerOptions.moduleResolution
4242
: compilerOptions.module === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic;
@@ -47,7 +47,7 @@ namespace ts {
4747
}
4848
}
4949

50-
export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModule {
50+
export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
5151
let containingDirectory = getDirectoryPath(containingFile);
5252

5353
if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) {
@@ -56,11 +56,13 @@ namespace ts {
5656
let resolvedFileName = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ false, failedLookupLocations, host);
5757

5858
if (resolvedFileName) {
59-
return { resolvedFileName, failedLookupLocations };
59+
return { resolvedModule: { resolvedFileName }, failedLookupLocations };
6060
}
6161

6262
resolvedFileName = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ false, failedLookupLocations, host);
63-
return { resolvedFileName, failedLookupLocations };
63+
return resolvedFileName
64+
? { resolvedModule: { resolvedFileName }, failedLookupLocations }
65+
: { resolvedModule: undefined, failedLookupLocations };
6466
}
6567
else {
6668
return loadModuleFromNodeModules(moduleName, containingDirectory, host);
@@ -117,7 +119,7 @@ namespace ts {
117119
return loadNodeModuleFromFile(combinePaths(candidate, "index"), loadOnlyDts, failedLookupLocation, host);
118120
}
119121

120-
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModule {
122+
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
121123
let failedLookupLocations: string[] = [];
122124
directory = normalizeSlashes(directory);
123125
while (true) {
@@ -127,12 +129,12 @@ namespace ts {
127129
let candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
128130
let result = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ true, failedLookupLocations, host);
129131
if (result) {
130-
return { resolvedFileName: result, failedLookupLocations };
132+
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
131133
}
132134

133135
result = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ true, failedLookupLocations, host);
134136
if (result) {
135-
return { resolvedFileName: result, failedLookupLocations };
137+
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
136138
}
137139
}
138140

@@ -144,47 +146,19 @@ namespace ts {
144146
directory = parentPath;
145147
}
146148

147-
return { resolvedFileName: undefined, failedLookupLocations };
148-
}
149-
150-
export function baseUrlModuleNameResolver(moduleName: string, containingFile: string, baseUrl: string, host: ModuleResolutionHost): ResolvedModule {
151-
Debug.assert(baseUrl !== undefined);
152-
153-
let normalizedModuleName = normalizeSlashes(moduleName);
154-
let basePart = useBaseUrl(moduleName) ? baseUrl : getDirectoryPath(containingFile);
155-
let candidate = normalizePath(combinePaths(basePart, moduleName));
156-
157-
let failedLookupLocations: string[] = [];
158-
159-
return forEach(supportedExtensions, ext => tryLoadFile(candidate + ext)) || { resolvedFileName: undefined, failedLookupLocations };
160-
161-
function tryLoadFile(location: string): ResolvedModule {
162-
if (host.fileExists(location)) {
163-
return { resolvedFileName: location, failedLookupLocations };
164-
}
165-
else {
166-
failedLookupLocations.push(location);
167-
return undefined;
168-
}
169-
}
149+
return { resolvedModule: undefined, failedLookupLocations };
170150
}
171151

172152
function nameStartsWithDotSlashOrDotDotSlash(name: string) {
173153
let i = name.lastIndexOf("./", 1);
174154
return i === 0 || (i === 1 && name.charCodeAt(0) === CharacterCodes.dot);
175155
}
176-
177-
function useBaseUrl(moduleName: string): boolean {
178-
// path is not rooted
179-
// module name does not start with './' or '../'
180-
return getRootLength(moduleName) === 0 && !nameStartsWithDotSlashOrDotDotSlash(moduleName);
181-
}
182156

183-
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule {
157+
export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
184158

185159
// module names that contain '!' are used to reference resources and are not resolved to actual files on disk
186160
if (moduleName.indexOf('!') != -1) {
187-
return { resolvedFileName: undefined, failedLookupLocations: [] };
161+
return { resolvedModule: undefined, failedLookupLocations: [] };
188162
}
189163

190164
let searchPath = getDirectoryPath(containingFile);
@@ -222,7 +196,9 @@ namespace ts {
222196
searchPath = parentPath;
223197
}
224198

225-
return { resolvedFileName: referencedSourceFile, failedLookupLocations };
199+
return referencedSourceFile
200+
? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations }
201+
: { resolvedModule: undefined, failedLookupLocations };
226202
}
227203

228204
/* @internal */
@@ -372,9 +348,9 @@ namespace ts {
372348

373349
host = host || createCompilerHost(options);
374350

375-
const resolveModuleNamesWorker =
376-
host.resolveModuleNames ||
377-
((moduleNames, containingFile) => map(moduleNames, moduleName => resolveModuleName(moduleName, containingFile, options, host).resolvedFileName));
351+
const resolveModuleNamesWorker = host.resolveModuleNames
352+
? ((moduleNames: string[], containingFile: string) => host.resolveModuleNames(moduleNames, containingFile))
353+
: ((moduleNames: string[], containingFile: string) => map(moduleNames, moduleName => resolveModuleName(moduleName, containingFile, options, host).resolvedModule));
378354

379355
let filesByName = createFileMap<SourceFile>(fileName => host.getCanonicalFileName(fileName));
380356

@@ -494,10 +470,17 @@ namespace ts {
494470
let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName);
495471
// ensure that module resolution results are still correct
496472
for (let i = 0; i < moduleNames.length; ++i) {
497-
let oldResolution = getResolvedModuleFileName(oldSourceFile, moduleNames[i]);
498-
if (oldResolution !== resolutions[i]) {
473+
let newResolution = resolutions[i];
474+
let oldResolution = getResolvedModule(oldSourceFile, moduleNames[i]);
475+
let resolutionChanged = oldResolution
476+
? !newResolution ||
477+
oldResolution.resolvedFileName !== newResolution.resolvedFileName ||
478+
!!oldResolution.isExternalLibraryImport !== !!newResolution.isExternalLibraryImport
479+
: newResolution;
480+
481+
if (resolutionChanged) {
499482
return false;
500-
}
483+
}
501484
}
502485
}
503486
// pass the cache of module resolutions from the old source file
@@ -874,9 +857,23 @@ namespace ts {
874857
let resolutions = resolveModuleNamesWorker(moduleNames, file.fileName);
875858
for (let i = 0; i < file.imports.length; ++i) {
876859
let resolution = resolutions[i];
877-
setResolvedModuleName(file, moduleNames[i], resolution);
860+
setResolvedModule(file, moduleNames[i], resolution);
878861
if (resolution && !options.noResolve) {
879-
findModuleSourceFile(resolution, file.imports[i]);
862+
const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i]);
863+
if (importedFile && resolution.isExternalLibraryImport) {
864+
if (!isExternalModule(importedFile)) {
865+
let start = getTokenPosOfNode(file.imports[i], file)
866+
fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.File_0_is_not_a_module, importedFile.fileName));
867+
}
868+
else if (!fileExtensionIs(importedFile.fileName, ".d.ts")) {
869+
let start = getTokenPosOfNode(file.imports[i], file)
870+
fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.Exported_external_package_typings_can_only_be_in_d_ts_files_Please_contact_the_package_author_to_update_the_package_definition));
871+
}
872+
else if (importedFile.referencedFiles.length) {
873+
let firstRef = importedFile.referencedFiles[0];
874+
fileProcessingDiagnostics.add(createFileDiagnostic(importedFile, firstRef.pos, firstRef.end - firstRef.pos, Diagnostics.Exported_external_package_typings_file_cannot_contain_tripleslash_references_Please_contact_the_package_author_to_update_the_package_definition));
875+
}
876+
}
880877
}
881878
}
882879
}

src/compiler/types.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@ namespace ts {
12841284
// Stores a mapping 'external module reference text' -> 'resolved file name' | undefined
12851285
// It is used to resolve module names in the checker.
12861286
// Content of this fiels should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead
1287-
/* @internal */ resolvedModules: Map<string>;
1287+
/* @internal */ resolvedModules: Map<ResolvedModule>;
12881288
/* @internal */ imports: LiteralExpression[];
12891289
}
12901290

@@ -2283,11 +2283,20 @@ namespace ts {
22832283

22842284
export interface ResolvedModule {
22852285
resolvedFileName: string;
2286+
/*
2287+
* Denotes if 'resolvedFileName' is isExternalLibraryImport and thus should be proper external module:
2288+
* - be a .d.ts file
2289+
* - use top level imports\exports
2290+
* - don't use tripleslash references
2291+
*/
2292+
isExternalLibraryImport?: boolean;
2293+
}
2294+
2295+
export interface ResolvedModuleWithFailedLookupLocations {
2296+
resolvedModule: ResolvedModule;
22862297
failedLookupLocations: string[];
22872298
}
22882299

2289-
export type ModuleNameResolver = (moduleName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => ResolvedModule;
2290-
22912300
export interface CompilerHost extends ModuleResolutionHost {
22922301
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
22932302
getCancellationToken?(): CancellationToken;
@@ -2305,7 +2314,7 @@ namespace ts {
23052314
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
23062315
* 'throw new Error("NotImplemented")'
23072316
*/
2308-
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
2317+
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
23092318
}
23102319

23112320
export interface TextSpan {

src/compiler/utilities.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,20 @@ namespace ts {
9999
return true;
100100
}
101101

102-
export function hasResolvedModuleName(sourceFile: SourceFile, moduleNameText: string): boolean {
102+
export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean {
103103
return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText);
104104
}
105105

106-
export function getResolvedModuleFileName(sourceFile: SourceFile, moduleNameText: string): string {
107-
return hasResolvedModuleName(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined;
106+
export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule {
107+
return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined;
108108
}
109109

110-
export function setResolvedModuleName(sourceFile: SourceFile, moduleNameText: string, resolvedFileName: string): void {
110+
export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void {
111111
if (!sourceFile.resolvedModules) {
112112
sourceFile.resolvedModules = {};
113113
}
114114

115-
sourceFile.resolvedModules[moduleNameText] = resolvedFileName;
115+
sourceFile.resolvedModules[moduleNameText] = resolvedModule;
116116
}
117117

118118
// Returns true if this node contains a parse error anywhere underneath it.

src/harness/harnessLanguageService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ module Harness.LanguageService {
225225
let imports: ts.Map<string> = {};
226226
for (let module of preprocessInfo.importedFiles) {
227227
let resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
228-
if (resolutionInfo.resolvedFileName) {
229-
imports[module.fileName] = resolutionInfo.resolvedFileName;
228+
if (resolutionInfo.resolvedModule) {
229+
imports[module.fileName] = resolutionInfo.resolvedModule.resolvedFileName;
230230
}
231231
}
232232
return JSON.stringify(imports);

0 commit comments

Comments
 (0)