8000 Check if imported file is a proper external module · FinalLody/TypeScript@e0c16b4 · GitHub
[go: up one dir, main page]

Skip to content

Commit e0c16b4

Browse files
committed
Check if imported file is a proper external module
1 parent a5c7201 commit e0c16b4

28 files changed

+347
-145
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
@@ -428,6 +428,8 @@ namespace ts {
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." },
430430
Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1: { code: 2653, category: DiagnosticCategory.Error, key: "Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'." },
431+
Proper_external_module_that_carries_external_typings_cannot_contain_tripleslash_references: { code: 2654, category: DiagnosticCategory.Error, key: "Proper external module that carries external typings cannot contain tripleslash references." },
432+
Proper_external_module_that_carries_external_typings_should_be_d_ts_file: { code: 2655, category: DiagnosticCategory.Error, key: "Proper external module that carries external typings should be '.d.ts' file." },
431433
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
432434
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}'." },
433435
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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1701,7 +1701,14 @@
17011701
"category": "Error",
17021702
"code": 2653
17031703
},
1704-
1704+
"Proper external module that carries external typings cannot contain tripleslash references.": {
1705+
"category": "Error",
1706+
"code": 2654
1707+
},
1708+
"Proper external module that carries external typings should be '.d.ts' file.": {
1709+
"category": "Error",
1710+
"code": 2655
1711+
},
17051712
"Import declaration '{0}' is using private name '{1}'.": {
17061713
"category": "Error",
17071714
"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, shouldBeProperExternalModule: 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, shouldBeProperExternalModule: 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 */
@@ -371,9 +347,9 @@ namespace ts {
371347

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

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

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

@@ -491,10 +467,17 @@ namespace ts {
491467
let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName);
492468
// ensure that module resolution results are still correct
493469
for (let i = 0; i < moduleNames.length; ++i) {
494-
let oldResolution = getResolvedModuleFileName(oldSourceFile, moduleNames[i]);
495-
if (oldResolution !== resolutions[i]) {
470+
let newResolution = resolutions[i];
471+
let oldResolution = getResolvedModule(oldSourceFile, moduleNames[i]);
472+
let resolutionChanged = oldResolution
473+
? !newResolution ||
474+
oldResolution.resolvedFileName !== newResolution.resolvedFileName ||
475+
!!oldResolution.shouldBeProperExternalModule !== !!newResolution.shouldBeProperExternalModule
476+
: newResolution;
477+
478+
if (resolutionChanged) {
496479
return false;
497-
}
480+
}
498481
}
499482
}
500483
// pass the cache of module resolutions from the old source file
@@ -864,9 +847,23 @@ namespace ts {
864847
let resolutions = resolveModuleNamesWorker(moduleNames, file.fileName);
865848
for (let i = 0; i < file.imports.length; ++i) {
866849
let resolution = resolutions[i];
867-
setResolvedModuleName(file, moduleNames[i], resolution);
850+
setResolvedModule(file, moduleNames[i], resolution);
868851
if (resolution && !options.noResolve) {
869-
findModuleSourceFile(resolution, file.imports[i]);
852+
const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i]);
853+
if (importedFile && resolution.shouldBeProperExternalModule) {
854+
if (!isExternalModule(importedFile)) {
855+
let start = getTokenPosOfNode(file.imports[i], file)
856+
diagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.File_0_is_not_a_module, importedFile.fileName));
857+
}
858+
else if (!fileExtensionIs(importedFile.fileName, ".d.ts")) {
859+
let start = getTokenPosOfNode(file.imports[i], file)
860+
diagnostics.add(createFileDiagnostic(file, start, fil 10000 e.imports[i].end - start, Diagnostics.Proper_external_module_that_carries_external_typings_should_be_d_ts_file));
861+
}
862+
else if (importedFile.referencedFiles.length) {
863+
let firstRef = importedFile.referencedFiles[0];
864+
diagnostics.add(createFileDiagnostic(importedFile, firstRef.pos, firstRef.end - firstRef.pos, Diagnostics.Proper_external_module_that_carries_external_typings_cannot_contain_tripleslash_references));
865+
}
866+
}
870867
}
871868
}
872869
}

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

@@ -2269,11 +2269,20 @@ namespace ts {
22692269

22702270
export interface ResolvedModule {
22712271
resolvedFileName: string;
2272+
/*
2273+
* Denotes if 'resolvedFileName' should be proper external module:
2274+
* - be a .d.ts file
2275+
* - use top level imports\exports
2276+
* - don't use tripleslash references
2277+
*/
2278+
shouldBeProperExternalModule?: boolean;
2279+
}
2280+
2281+
export interface ResolvedModuleWithFailedLookupLocations {
2282+
resolvedModule: ResolvedModule;
22722283
failedLookupLocations: string[];
22732284
}
22742285

2275-
export type ModuleNameResolver = (moduleName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => ResolvedModule;
2276-
22772286
export interface CompilerHost extends ModuleResolutionHost {
22782287
getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile;
22792288
getCancellationToken?(): CancellationToken;
@@ -2291,7 +2300,7 @@ namespace ts {
22912300
* If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just
22922301
* 'throw new Error("NotImplemented")'
22932302
*/
2294-
resolveModuleNames?(moduleNames: string[], containingFile: string): string[];
2303+
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
22952304
}
22962305

22972306
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)
0