diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3f79f148458e0..c49ff726ed4a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -976,8 +976,8 @@ namespace ts { } } - let fileName = getResolvedModuleFileName(getSourceFile(location), moduleReferenceLiteral.text); - let sourceFile = fileName && host.getSourceFile(fileName); + let resolvedModule = getResolvedModule(getSourceFile(location), moduleReferenceLiteral.text); + let sourceFile = resolvedModule && host.getSourceFile(resolvedModule.resolvedFileName); if (sourceFile) { if (sourceFile.symbol) { return sourceFile.symbol; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index af48e67d964d7..3775c9ce40510 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -428,6 +428,8 @@ namespace ts { 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." }, 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." }, 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}'." }, + 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." }, + 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." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, 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}'." }, 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}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7abec7b7c43cf..79fd114e9a362 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1701,7 +1701,14 @@ "category": "Error", "code": 2653 }, - + "Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition.": { + "category": "Error", + "code": 2654 + }, + "Exported external package typings can only be in '.d.ts' files. Please contact the package author to update the package definition.": { + "category": "Error", + "code": 2655 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d86a80a5387f5..28dd477666b56 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -36,7 +36,7 @@ namespace ts { return normalizePath(referencedFileName); } - export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { + export function resolveModuleName(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { let moduleResolution = compilerOptions.moduleResolution !== undefined ? compilerOptions.moduleResolution : compilerOptions.module === ModuleKind.CommonJS ? ModuleResolutionKind.NodeJs : ModuleResolutionKind.Classic; @@ -47,7 +47,7 @@ namespace ts { } } - export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModule { + export function nodeModuleNameResolver(moduleName: string, containingFile: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { let containingDirectory = getDirectoryPath(containingFile); if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) { @@ -56,11 +56,13 @@ namespace ts { let resolvedFileName = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ false, failedLookupLocations, host); if (resolvedFileName) { - return { resolvedFileName, failedLookupLocations }; + return { resolvedModule: { resolvedFileName }, failedLookupLocations }; } resolvedFileName = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ false, failedLookupLocations, host); - return { resolvedFileName, failedLookupLocations }; + return resolvedFileName + ? { resolvedModule: { resolvedFileName }, failedLookupLocations } + : { resolvedModule: undefined, failedLookupLocations }; } else { return loadModuleFromNodeModules(moduleName, containingDirectory, host); @@ -117,7 +119,7 @@ namespace ts { return loadNodeModuleFromFile(combinePaths(candidate, "index"), loadOnlyDts, failedLookupLocation, host); } - function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModule { + function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { let failedLookupLocations: string[] = []; directory = normalizeSlashes(directory); while (true) { @@ -127,12 +129,12 @@ namespace ts { let candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName)); let result = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); if (result) { - return { resolvedFileName: result, failedLookupLocations }; + return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; } result = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); if (result) { - return { resolvedFileName: result, failedLookupLocations }; + return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; } } @@ -144,47 +146,19 @@ namespace ts { directory = parentPath; } - return { resolvedFileName: undefined, failedLookupLocations }; - } - - export function baseUrlModuleNameResolver(moduleName: string, containingFile: string, baseUrl: string, host: ModuleResolutionHost): ResolvedModule { - Debug.assert(baseUrl !== undefined); - - let normalizedModuleName = normalizeSlashes(moduleName); - let basePart = useBaseUrl(moduleName) ? baseUrl : getDirectoryPath(containingFile); - let candidate = normalizePath(combinePaths(basePart, moduleName)); - - let failedLookupLocations: string[] = []; - - return forEach(supportedExtensions, ext => tryLoadFile(candidate + ext)) || { resolvedFileName: undefined, failedLookupLocations }; - - function tryLoadFile(location: string): ResolvedModule { - if (host.fileExists(location)) { - return { resolvedFileName: location, failedLookupLocations }; - } - else { - failedLookupLocations.push(location); - return undefined; - } - } + return { resolvedModule: undefined, failedLookupLocations }; } function nameStartsWithDotSlashOrDotDotSlash(name: string) { let i = name.lastIndexOf("./", 1); return i === 0 || (i === 1 && name.charCodeAt(0) === CharacterCodes.dot); } - - function useBaseUrl(moduleName: string): boolean { - // path is not rooted - // module name does not start with './' or '../' - return getRootLength(moduleName) === 0 && !nameStartsWithDotSlashOrDotDotSlash(moduleName); - } - export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { + export function classicNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { // module names that contain '!' are used to reference resources and are not resolved to actual files on disk if (moduleName.indexOf('!') != -1) { - return { resolvedFileName: undefined, failedLookupLocations: [] }; + return { resolvedModule: undefined, failedLookupLocations: [] }; } let searchPath = getDirectoryPath(containingFile); @@ -222,7 +196,9 @@ namespace ts { searchPath = parentPath; } - return { resolvedFileName: referencedSourceFile, failedLookupLocations }; + return referencedSourceFile + ? { resolvedModule: { resolvedFileName: referencedSourceFile }, failedLookupLocations } + : { resolvedModule: undefined, failedLookupLocations }; } /* @internal */ @@ -372,9 +348,9 @@ namespace ts { host = host || createCompilerHost(options); - const resolveModuleNamesWorker = - host.resolveModuleNames || - ((moduleNames, containingFile) => map(moduleNames, moduleName => resolveModuleName(moduleName, containingFile, options, host).resolvedFileName)); + const resolveModuleNamesWorker = host.resolveModuleNames + ? ((moduleNames: string[], containingFile: string) => host.resolveModuleNames(moduleNames, containingFile)) + : ((moduleNames: string[], containingFile: string) => map(moduleNames, moduleName => resolveModuleName(moduleName, containingFile, options, host).resolvedModule)); let filesByName = createFileMap(fileName => host.getCanonicalFileName(fileName)); @@ -494,10 +470,17 @@ namespace ts { let resolutions = resolveModuleNamesWorker(moduleNames, newSourceFile.fileName); // ensure that module resolution results are still correct for (let i = 0; i < moduleNames.length; ++i) { - let oldResolution = getResolvedModuleFileName(oldSourceFile, moduleNames[i]); - if (oldResolution !== resolutions[i]) { + let newResolution = resolutions[i]; + let oldResolution = getResolvedModule(oldSourceFile, moduleNames[i]); + let resolutionChanged = oldResolution + ? !newResolution || + oldResolution.resolvedFileName !== newResolution.resolvedFileName || + !!oldResolution.isExternalLibraryImport !== !!newResolution.isExternalLibraryImport + : newResolution; + + if (resolutionChanged) { return false; - } + } } } // pass the cache of module resolutions from the old source file @@ -874,9 +857,23 @@ namespace ts { let resolutions = resolveModuleNamesWorker(moduleNames, file.fileName); for (let i = 0; i < file.imports.length; ++i) { let resolution = resolutions[i]; - setResolvedModuleName(file, moduleNames[i], resolution); + setResolvedModule(file, moduleNames[i], resolution); if (resolution && !options.noResolve) { - findModuleSourceFile(resolution, file.imports[i]); + const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i]); + if (importedFile && resolution.isExternalLibraryImport) { + if (!isExternalModule(importedFile)) { + let start = getTokenPosOfNode(file.imports[i], file) + fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.File_0_is_not_a_module, importedFile.fileName)); + } + else if (!fileExtensionIs(importedFile.fileName, ".d.ts")) { + let start = getTokenPosOfNode(file.imports[i], file) + 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)); + } + else if (importedFile.referencedFiles.length) { + let firstRef = importedFile.referencedFiles[0]; + 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)); + } + } } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 265fa5c86fbcf..40773b4fc23ce 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1284,7 +1284,7 @@ namespace ts { // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined // It is used to resolve module names in the checker. // Content of this fiels should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead - /* @internal */ resolvedModules: Map; + /* @internal */ resolvedModules: Map; /* @internal */ imports: LiteralExpression[]; } @@ -2270,11 +2270,20 @@ namespace ts { export interface ResolvedModule { resolvedFileName: string; + /* + * Denotes if 'resolvedFileName' is isExternalLibraryImport and thus should be proper external module: + * - be a .d.ts file + * - use top level imports\exports + * - don't use tripleslash references + */ + isExternalLibraryImport?: boolean; + } + + export interface ResolvedModuleWithFailedLookupLocations { + resolvedModule: ResolvedModule; failedLookupLocations: string[]; } - export type ModuleNameResolver = (moduleName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => ResolvedModule; - export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; getCancellationToken?(): CancellationToken; @@ -2292,7 +2301,7 @@ namespace ts { * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just * 'throw new Error("NotImplemented")' */ - resolveModuleNames?(moduleNames: string[], containingFile: string): string[]; + resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; } export interface TextSpan { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 76936836d0a92..ed8b516b7f30c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -99,20 +99,20 @@ namespace ts { return true; } - export function hasResolvedModuleName(sourceFile: SourceFile, moduleNameText: string): boolean { + export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); } - export function getResolvedModuleFileName(sourceFile: SourceFile, moduleNameText: string): string { - return hasResolvedModuleName(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined; + export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule { + return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined; } - export function setResolvedModuleName(sourceFile: SourceFile, moduleNameText: string, resolvedFileName: string): void { + export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void { if (!sourceFile.resolvedModules) { sourceFile.resolvedModules = {}; } - sourceFile.resolvedModules[moduleNameText] = resolvedFileName; + sourceFile.resolvedModules[moduleNameText] = resolvedModule; } // Returns true if this node contains a parse error anywhere underneath it. diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index f228bc862d88c..c97ce50d2753d 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -225,8 +225,8 @@ module Harness.LanguageService { let imports: ts.Map = {}; for (let module of preprocessInfo.importedFiles) { let resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); - if (resolutionInfo.resolvedFileName) { - imports[module.fileName] = resolutionInfo.resolvedFileName; + if (resolutionInfo.resolvedModule) { + imports[module.fileName] = resolutionInfo.resolvedModule.resolvedFileName; } } return JSON.stringify(imports); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 76bc586b8bbbb..7ab46fc689e41 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -79,7 +79,7 @@ namespace ts.server { } } - interface TimestampedResolvedModule extends ResolvedModule { + interface TimestampedResolvedModule extends ResolvedModuleWithFailedLookupLocations { lastCheckTime: number; } @@ -99,11 +99,11 @@ namespace ts.server { } } - resolveModuleNames(moduleNames: string[], containingFile: string): string[] { + resolveModuleNames(moduleNames: string[], containingFile: string): ResolvedModule[] { let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile); let newResolutions: Map = {}; - let resolvedFileNames: string[] = []; + let resolvedModules: ResolvedModule[] = []; let compilerOptions = this.getCompilationSettings(); @@ -119,25 +119,25 @@ namespace ts.server { else { resolution = resolveModuleName(moduleName, containingFile, compilerOptions, this.moduleResolutionHost); resolution.lastCheckTime = Date.now(); - newResolutions[moduleName] = resolution; + newResolutions[moduleName] = resolution; } } ts.Debug.assert(resolution !== undefined); - resolvedFileNames.push(resolution.resolvedFileName); + resolvedModules.push(resolution.resolvedModule); } // replace old results with a new one this.resolvedModuleNames.set(containingFile, newResolutions); - return resolvedFileNames; + return resolvedModules; function moduleResolutionIsValid(resolution: TimestampedResolvedModule): boolean { if (!resolution) { return false; } - if (resolution.resolvedFileName) { + if (resolution.resolvedModule) { // TODO: consider checking failedLookupLocations // TODO: use lastCheckTime to track expiration for module name resolution return true; diff --git a/src/services/services.ts b/src/services/services.ts index d5ede917b8f78..9fbcc05a5ff90 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -802,7 +802,7 @@ namespace ts { public languageVariant: LanguageVariant; public identifiers: Map; public nameTable: Map; - public resolvedModules: Map; + public resolvedModules: Map; public imports: LiteralExpression[]; private namedDeclarations: Map; @@ -1022,7 +1022,7 @@ namespace ts { * if implementation is omitted then language service will use built-in module resolution logic and get answers to * host specific questions using 'getScriptSnapshot'. */ - resolveModuleNames?(moduleNames: string[], containingFile: string): string[]; + resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[]; } // diff --git a/src/services/shims.ts b/src/services/shims.ts index 307062cebd8cc..351514a1499bb 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -273,7 +273,7 @@ namespace ts { private loggingEnabled = false; private tracingEnabled = false; - public resolveModuleNames: (moduleName: string[], containingFile: string) => string[]; + public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[]; constructor(private shimHost: LanguageServiceShimHost) { // if shimHost is a COM object then property check will become method call with no arguments. @@ -281,7 +281,10 @@ namespace ts { if ("getModuleResolutionsForFile" in this.shimHost) { this.resolveModuleNames = (moduleNames: string[], containingFile: string) => { let resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile)); - return map(moduleNames, name => lookUp(resolutionsInFile, name)); + return map(moduleNames, name => { + const result = lookUp(resolutionsInFile, name); + return result ? { resolvedFileName: result } : undefined; + }); }; } } @@ -942,7 +945,11 @@ namespace ts { public resolveModuleName(fileName: string, moduleName: string, compilerOptionsJson: string): string { return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => { let compilerOptions = JSON.parse(compilerOptionsJson); - return resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host); + const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host); + return { + resolvedFileName: result.resolvedModule ? result.resolvedModule.resolvedFileName: undefined, + failedLookupLocations: result.failedLookupLocations + }; }); } diff --git a/tests/baselines/reference/nodeResolution4.js b/tests/baselines/reference/nodeResolution4.js new file mode 100644 index 0000000000000..0965c257da9fe --- /dev/null +++ b/tests/baselines/reference/nodeResolution4.js @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/nodeResolution4.ts] //// + +//// [ref.ts] + +var x = 1; + +//// [a.ts] +/// +export var y; + +//// [b.ts] +import y = require("./a"); + +//// [ref.js] +var x = 1; +//// [a.js] +//// [b.js] diff --git a/tests/baselines/reference/nodeResolution4.symbols b/tests/baselines/reference/nodeResolution4.symbols new file mode 100644 index 0000000000000..b8e0b59284a9f --- /dev/null +++ b/tests/baselines/reference/nodeResolution4.symbols @@ -0,0 +1,14 @@ +=== tests/cases/compiler/b.ts === +import y = require("./a"); +>y : Symbol(y, Decl(b.ts, 0, 0)) + +=== tests/cases/compiler/ref.ts === + +var x = 1; +>x : Symbol(x, Decl(ref.ts, 1, 3)) + +=== tests/cases/compiler/a.ts === +/// +export var y; +>y : Symbol(y, Decl(a.ts, 1, 10)) + diff --git a/tests/baselines/reference/nodeResolution4.types b/tests/baselines/reference/nodeResolution4.types new file mode 100644 index 0000000000000..b273b203bde6b --- /dev/null +++ b/tests/baselines/reference/nodeResolution4.types @@ -0,0 +1,15 @@ +=== tests/cases/compiler/b.ts === +import y = require("./a"); +>y : typeof y + +=== tests/cases/compiler/ref.ts === + +var x = 1; +>x : number +>1 : number + +=== tests/cases/compiler/a.ts === +/// +export var y; +>y : any + diff --git a/tests/baselines/reference/nodeResolution5.errors.txt b/tests/baselines/reference/nodeResolution5.errors.txt new file mode 100644 index 0000000000000..1fc4e1cee525b --- /dev/null +++ b/tests/baselines/reference/nodeResolution5.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/b.ts(1,20): error TS2306: File 'tests/cases/compiler/node_modules/a.d.ts' is not a module. + + +==== tests/cases/compiler/b.ts (1 errors) ==== + import y = require("a"); + ~~~ +!!! error TS2306: File 'a.d.ts' is not a module. + +==== tests/cases/compiler/node_modules/a.d.ts (0 errors) ==== + + declare module "a" { + var x: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/nodeResolution5.js b/tests/baselines/reference/nodeResolution5.js new file mode 100644 index 0000000000000..2bc009c15223f --- /dev/null +++ b/tests/baselines/reference/nodeResolution5.js @@ -0,0 +1,13 @@ +//// [tests/cases/compiler/nodeResolution5.ts] //// + +//// [a.d.ts] + +declare module "a" { + var x: number; +} + +//// [b.ts] +import y = require("a"); + + +//// [b.js] diff --git a/tests/baselines/reference/nodeResolution6.errors.txt b/tests/baselines/reference/nodeResolution6.errors.txt new file mode 100644 index 0000000000000..6bab6e34389d5 --- /dev/null +++ b/tests/baselines/reference/nodeResolution6.errors.txt @@ -0,0 +1,17 @@ +tests/cases/compiler/node_modules/a.d.ts(1,1): error TS2654: Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition. + + +==== tests/cases/compiler/b.ts (0 errors) ==== + import y = require("a"); + +==== tests/cases/compiler/node_modules/ref.ts (0 errors) ==== + + var x = 1; + +==== tests/cases/compiler/node_modules/a.d.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2654: Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition. + export declare var y; + + \ No newline at end of file diff --git a/tests/baselines/reference/nodeResolution6.js b/tests/baselines/reference/nodeResolution6.js new file mode 100644 index 0000000000000..140edd61f5a9f --- /dev/null +++ b/tests/baselines/reference/nodeResolution6.js @@ -0,0 +1,18 @@ +//// [tests/cases/compiler/nodeResolution6.ts] //// + +//// [ref.ts] + +var x = 1; + +//// [a.d.ts] +/// +export declare var y; + + +//// [b.ts] +import y = require("a"); + + +//// [ref.js] +var x = 1; +//// [b.js] diff --git a/tests/baselines/reference/nodeResolution7.errors.txt b/tests/baselines/reference/nodeResolution7.errors.txt new file mode 100644 index 0000000000000..1e1f822f0d419 --- /dev/null +++ b/tests/baselines/reference/nodeResolution7.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/b.ts(1,20): error TS2306: File 'tests/cases/compiler/node_modules/a/index.d.ts' is not a module. + + +==== tests/cases/compiler/b.ts (1 errors) ==== + import y = require("a"); + ~~~ +!!! error TS2306: File 'index.d.ts' is not a module. + +==== tests/cases/compiler/node_modules/a/index.d.ts (0 errors) ==== + + declare module "a" { + var x: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/nodeResolution7.js b/tests/baselines/reference/nodeResolution7.js new file mode 100644 index 0000000000000..abfbbad52a628 --- /dev/null +++ b/tests/baselines/reference/nodeResolution7.js @@ -0,0 +1,13 @@ +//// [tests/cases/compiler/nodeResolution7.ts] //// + +//// [index.d.ts] + +declare module "a" { + var x: number; +} + +//// [b.ts] +import y = require("a"); + + +//// [b.js] diff --git a/tests/baselines/reference/nodeResolution8.errors.txt b/tests/baselines/reference/nodeResolution8.errors.txt new file mode 100644 index 0000000000000..3f14a4313c8cd --- /dev/null +++ b/tests/baselines/reference/nodeResolution8.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/node_modules/a/index.d.ts(1,1): error TS2654: Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition. + + +==== tests/cases/compiler/b.ts (0 errors) ==== + import y = require("a"); +==== tests/cases/compiler/node_modules/a/ref.ts (0 errors) ==== + + var x = 1; + +==== tests/cases/compiler/node_modules/a/index.d.ts (1 errors) ==== + /// + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2654: Exported external package typings file cannot contain tripleslash references. Please contact the package author to update the package definition. + export declare var y; + + \ No newline at end of file diff --git a/tests/baselines/reference/nodeResolution8.js b/tests/baselines/reference/nodeResolution8.js new file mode 100644 index 0000000000000..aa67f4bf9ccb5 --- /dev/null +++ b/tests/baselines/reference/nodeResolution8.js @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/nodeResolution8.ts] //// + +//// [ref.ts] + +var x = 1; + +//// [index.d.ts] +/// +export declare var y; + + +//// [b.ts] +import y = require("a"); + +//// [ref.js] +var x = 1; +//// [b.js] diff --git a/tests/cases/compiler/nodeResolution4.ts b/tests/cases/compiler/nodeResolution4.ts new file mode 100644 index 0000000000000..247546c3f0cfc --- /dev/null +++ b/tests/cases/compiler/nodeResolution4.ts @@ -0,0 +1,12 @@ +// @module: commonjs +// @moduleResolution: node + +// @filename: ref.ts +var x = 1; + +// @filename: a.ts +/// +export var y; + +// @filename: b.ts +import y = require("./a"); \ No newline at end of file diff --git a/tests/cases/compiler/nodeResolution5.ts b/tests/cases/compiler/nodeResolution5.ts new file mode 100644 index 0000000000000..313dabd7899ff --- /dev/null +++ b/tests/cases/compiler/nodeResolution5.ts @@ -0,0 +1,10 @@ +// @module: commonjs +// @moduleResolution: node + +// @filename: node_modules/a.d.ts +declare module "a" { + var x: number; +} + +// @filename: b.ts +import y = require("a"); diff --git a/tests/cases/compiler/nodeResolution6.ts b/tests/cases/compiler/nodeResolution6.ts new file mode 100644 index 0000000000000..3f6dc81c84780 --- /dev/null +++ b/tests/cases/compiler/nodeResolution6.ts @@ -0,0 +1,13 @@ +// @module: commonjs +// @moduleResolution: node + +// @filename: node_modules/ref.ts +var x = 1; + +// @filename: node_modules/a.d.ts +/// +export declare var y; + + +// @filename: b.ts +import y = require("a"); diff --git a/tests/cases/compiler/nodeResolution7.ts b/tests/cases/compiler/nodeResolution7.ts new file mode 100644 index 0000000000000..b4283edb76ad3 --- /dev/null +++ b/tests/cases/compiler/nodeResolution7.ts @@ -0,0 +1,10 @@ +// @module: commonjs +// @moduleResolution: node + +// @filename: node_modules/a/index.d.ts +declare module "a" { + var x: number; +} + +// @filename: b.ts +import y = require("a"); diff --git a/tests/cases/compiler/nodeResolution8.ts b/tests/cases/compiler/nodeResolution8.ts new file mode 100644 index 0000000000000..0a6e528f20ae3 --- /dev/null +++ b/tests/cases/compiler/nodeResolution8.ts @@ -0,0 +1,13 @@ +// @module: commonjs +// @moduleResolution: node + +// @filename: node_modules/a/ref.ts +var x = 1; + +// @filename: node_modules/a/index.d.ts +/// +export declare var y; + + +// @filename: b.ts +import y = require("a"); \ No newline at end of file diff --git a/tests/cases/unittests/moduleResolution.ts b/tests/cases/unittests/moduleResolution.ts index 0917e72f1ce4d..ed9f0b0a9863e 100644 --- a/tests/cases/unittests/moduleResolution.ts +++ b/tests/cases/unittests/moduleResolution.ts @@ -40,8 +40,9 @@ module ts { let containingFile = { name: containingFileName } let moduleFile = { name: moduleFileNameNoExt + ext } let resolution = nodeModuleNameResolver(moduleName, containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); - + assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name); + assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false); + let failedLookupLocations: string[] = []; let dir = getDirectoryPath(containingFileName); for (let e of supportedExtensions) { @@ -78,7 +79,8 @@ module ts { let packageJson = { name: packageJsonFileName, content: JSON.stringify({ "typings": fieldRef }) }; let moduleFile = { name: moduleFileName }; let resolution = nodeModuleNameResolver(moduleName, containingFile.name, createModuleResolutionHost(containingFile, packageJson, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); + assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name); + assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false); // expect three failed lookup location - attempt to load module as file with all supported extensions assert.equal(resolution.failedLookupLocations.length, 3); } @@ -95,7 +97,8 @@ module ts { let packageJson = {name: "/a/b/foo/package.json", content: JSON.stringify({main: "/c/d"})}; let indexFile = { name: "/a/b/foo/index.d.ts" }; let resolution = nodeModuleNameResolver("./foo", containingFile.name, createModuleResolutionHost(containingFile, packageJson, indexFile)); - assert.equal(resolution.resolvedFileName, indexFile.name); + assert.equal(resolution.resolvedModule.resolvedFileName, indexFile.name); + assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false); assert.deepEqual(resolution.failedLookupLocations, [ "/a/b/foo.ts", "/a/b/foo.tsx", @@ -111,7 +114,7 @@ module ts { let containingFile = { name: "/a/b/c/d/e.ts" }; let moduleFile = { name: "/a/b/node_modules/foo.ts" }; let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, undefined); + assert.equal(resolution.resolvedModule, undefined); assert.deepEqual(resolution.failedLookupLocations, [ "/a/b/c/d/node_modules/foo.d.ts", "/a/b/c/d/node_modules/foo/package.json", @@ -135,14 +138,16 @@ module ts { let containingFile = { name: "/a/b/c/d/e.ts" }; let moduleFile = { name: "/a/b/node_modules/foo.d.ts" }; let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); + assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name); + assert.equal(resolution.resolvedModule.isExternalLibraryImport, true); }); it("load module as directory", () => { let containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" }; let moduleFile = { name: "/a/node_modules/foo/index.d.ts" }; let resolution = nodeModuleNameResolver("foo", containingFile.name, createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); + assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name); + assert.equal(resolution.resolvedModule.isExternalLibraryImport, true); assert.deepEqual(resolution.failedLookupLocations, [ "/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts", "/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json", @@ -158,64 +163,4 @@ module ts { ]); }); }); - - describe("BaseUrl mode", () => { - - it ("load module as relative url", () => { - function test(containingFileName: string, moduleFileName: string, moduleName: string): void { - let containingFile = {name: containingFileName }; - let moduleFile = { name: moduleFileName }; - let resolution = baseUrlModuleNameResolver(moduleName, containingFile.name, "", createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); - let expectedFailedLookupLocations: string[] = []; - - let moduleNameHasExt = forEach(supportedExtensions, e => fileExtensionIs(moduleName, e)); - if (!moduleNameHasExt) { - let dir = getDirectoryPath(containingFileName); - - // add candidates with extensions that precede extension of the actual module name file in the list of supportd extensions - for (let ext of supportedExtensions) { - - let hasExtension = ext !== ".ts" - ? fileExtensionIs(moduleFileName, ext) - : fileExtensionIs(moduleFileName, ".ts") && !fileExtensionIs(moduleFileName, ".d.ts"); - - if (hasExtension) { - break; - } - else { - expectedFailedLookupLocations.push(normalizePath(combinePaths(dir, moduleName + ext))); - } - } - } - - assert.deepEqual(resolution.failedLookupLocations, expectedFailedLookupLocations) - } - - test("/a/b/c/d.ts", "/foo.ts", "/foo"); - test("/a/b/c/d.ts", "/foo.d.ts", "/foo"); - test("/a/b/c/d.ts", "/foo.tsx", "/foo"); - - test("/a/b/c/d.ts", "/a/b/c/foo.ts", "./foo"); - test("/a/b/c/d.ts", "/a/b/c/foo.d.ts", "./foo"); - test("/a/b/c/d.ts", "/a/b/c/foo.tsx", "./foo"); - - test("/a/b/c/d.ts", "/a/b/foo.ts", "../foo"); - test("/a/b/c/d.ts", "/a/b/foo.d.ts", "../foo"); - test("/a/b/c/d.ts", "/a/b/foo.tsx", "../foo"); - }); - - it ("load module using base url", () => { - function test(containingFileName: string, moduleFileName: string, moduleName: string, baseUrl: string): void { - let containingFile = { name: containingFileName }; - let moduleFile = { name: moduleFileName }; - let resolution = baseUrlModuleNameResolver(moduleName, containingFileName, baseUrl, createModuleResolutionHost(containingFile, moduleFile)); - assert.equal(resolution.resolvedFileName, moduleFile.name); - } - - test("/a/base/c/d.ts", "/a/base/c/d/e.ts", "c/d/e", "/a/base"); - test("/a/base/c/d.ts", "/a/base/c/d/e.d.ts", "c/d/e", "/a/base"); - test("/a/base/c/d.ts", "/a/base/c/d/e.tsx", "c/d/e", "/a/base"); - }); - }); } \ No newline at end of file diff --git a/tests/cases/unittests/reuseProgramStructure.ts b/tests/cases/unittests/reuseProgramStructure.ts index 56b1dedbcbbdc..5f313eeae2b32 100644 --- a/tests/cases/unittests/reuseProgramStructure.ts +++ b/tests/cases/unittests/reuseProgramStructure.ts @@ -160,7 +160,7 @@ module ts { return size; } - function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map): void { + function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map): void { let file = program.getSourceFile(fileName); assert.isTrue(file !== undefined, `cannot find file ${fileName}`); if (expectedContent === undefined) { @@ -175,7 +175,16 @@ module ts { for (let id in expectedContent) { if (hasProperty(expectedContent, id)) { assert.isTrue(hasProperty(file.resolvedModules, id), `expected ${id} to be found in resolved modules`); - assert.isTrue(expectedContent[id] === file.resolvedModules[id], `expected '${expectedContent[id]}' to be equal to '${file.resolvedModules[id]}'`); + if (expectedContent[id]) { + const expected = expectedContent[id]; + const actual = file.resolvedModules[id]; + assert.isTrue(actual !== undefined); + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'shouldBeProperExternalModule': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); + } + else { + assert.isTrue(file.resolvedModules[id] === undefined); + } } } } @@ -237,7 +246,7 @@ module ts { var options: CompilerOptions = { target }; var program_1 = newProgram(files, ["a.ts"], options); - checkResolvedModulesCache(program_1, "a.ts", { "b": "b.ts" }); + checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); checkResolvedModulesCache(program_1, "b.ts", undefined); var program_2 = updateProgram(program_1, ["a.ts"], options, files => { @@ -246,7 +255,7 @@ module ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedModulesCache(program_1, "a.ts", { "b": "b.ts" }); + checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); checkResolvedModulesCache(program_1, "b.ts", undefined); // imports has changed - program is not reused @@ -263,7 +272,7 @@ module ts { files[0].text = files[0].text.updateImportsAndExports(newImports); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedModulesCache(program_4, "a.ts", { "b": "b.ts", "c": undefined }); + checkResolvedModulesCache(program_4, "a.ts", { "b": { resolvedFileName: "b.ts" }, "c": undefined }); }); }) } \ No newline at end of file