-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Package scopes #4913
Package scopes #4913
Changes from 10 commits
ecf7c1d
c232afc
b2cd788
02166e2
cb6e430
3bfd92a
314cfc1
c89ef0b
ffb38da
ab269f6
1e32030
ae158ad
3e58162
f86c28d
4224586
149a1d9
22c6377
312d97c
b1ea98c
d32cedb
340702e
41c21b5
fd33df3
733db05
1163d38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,7 +125,9 @@ namespace ts { | |
let anySignature = createSignature(undefined, undefined, emptyArray, anyType, undefined, 0, false, false); | ||
let unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, undefined, 0, false, false); | ||
|
||
let globals: SymbolTable = {}; | ||
let globalScope: Scope = { | ||
symbols: {} | ||
}; | ||
|
||
let globalESSymbolConstructorSymbol: Symbol; | ||
|
||
|
@@ -426,6 +428,12 @@ namespace ts { | |
} | ||
switch (location.kind) { | ||
case SyntaxKind.SourceFile: | ||
if ((<SourceFile>location).package) { | ||
if (hasProperty((<SourceFile>location).package.symbols, name)) { | ||
result = (<SourceFile>location).package.symbols[name]; | ||
break loop; | ||
} | ||
} | ||
if (!isExternalModule(<SourceFile>location)) break; | ||
case SyntaxKind.ModuleDeclaration: | ||
let moduleExports = getSymbolOfNode(location).exports; | ||
|
@@ -578,7 +586,7 @@ namespace ts { | |
} | ||
|
||
if (!result) { | ||
result = getSymbol(globals, name, meaning); | ||
result = getSymbol(globalScope.symbols, name, meaning); | ||
} | ||
|
||
if (!result) { | ||
|
@@ -967,7 +975,8 @@ namespace ts { | |
} | ||
let isRelative = isExternalModuleNameRelative(moduleName); | ||
if (!isRelative) { | ||
let symbol = getSymbol(globals, "\"" + moduleName + "\"", SymbolFlags.ValueModule); | ||
let file = getSourceFileOfNode(location); | ||
let symbol = getSymbol(file.package ? file.package.symbols : globalScope.symbols, "\"" + moduleName + "\"", SymbolFlags.ValueModule); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (symbol) { | ||
return symbol; | ||
} | ||
|
@@ -1175,6 +1184,11 @@ namespace ts { | |
} | ||
switch (location.kind) { | ||
case SyntaxKind.SourceFile: | ||
if ((<SourceFile>location).package) { | ||
if (result = callback((<SourceFile>location).package.symbols)) { | ||
return result; | ||
} | ||
} | ||
if (!isExternalModule(<SourceFile>location)) { | ||
break; | ||
} | ||
|
@@ -1192,7 +1206,7 @@ namespace ts { | |
} | ||
} | ||
|
||
return callback(globals); | ||
return callback(globalScope.symbols); | ||
} | ||
|
||
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) { | ||
|
@@ -14094,6 +14108,9 @@ namespace ts { | |
|
||
switch (location.kind) { | ||
case SyntaxKind.SourceFile: | ||
if ((<SourceFile>location).package) { | ||
copySymbols((<SourceFile>location).package.symbols, meaning); | ||
} | ||
if (!isExternalModule(<SourceFile>location)) { | ||
break; | ||
} | ||
|
@@ -14136,7 +14153,7 @@ namespace ts { | |
location = location.parent; | ||
} | ||
|
||
copySymbols(globals, meaning); | ||
copySymbols(globalScope.symbols, meaning); | ||
} | ||
|
||
/** | ||
|
@@ -14762,7 +14779,11 @@ namespace ts { | |
} | ||
|
||
function hasGlobalName(name: string): boolean { | ||
return hasProperty(globals, name); | ||
return hasProperty(globalScope.symbols, name); | ||
} | ||
|
||
function hasPackageInternalName(file: SourceFile, name: string): boolean { | ||
return file.package && hasProperty(file.package.symbols, name); | ||
} | ||
|
||
function getReferencedValueSymbol(reference: Identifier): Symbol { | ||
|
@@ -14799,6 +14820,7 @@ namespace ts { | |
isNestedRedeclaration, | ||
isValueAliasDeclaration, | ||
hasGlobalName, | ||
hasPackageInternalName, | ||
isReferencedAliasDeclaration, | ||
getNodeCheckFlags, | ||
isTopLevelValueImportEqualsWithEntityName, | ||
|
@@ -14823,18 +14845,29 @@ namespace ts { | |
bindSourceFile(file); | ||
}); | ||
|
||
// Initialize global symbol table | ||
let packages: Map<PackageDescriptor> = {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// Initialize package/global symbol table(s) | ||
forEach(host.getSourceFiles(), file => { | ||
if (!isExternalModule(file)) { | ||
mergeSymbolTable(globals, file.locals); | ||
if (file.package) { | ||
if (!packages[file.package.packageFile]) { | ||
packages[file.package.packageFile] = file.package; | ||
} | ||
file.package = packages[file.package.packageFile]; // Dedupe packages | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in an |
||
mergeSymbolTable(file.package.symbols, file.locals); | ||
} | ||
else { | ||
mergeSymbolTable(globalScope.symbols, file.locals); | ||
} | ||
} | ||
}); | ||
|
||
// Initialize special symbols | ||
getSymbolLinks(undefinedSymbol).type = undefinedType; | ||
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments"); | ||
getSymbolLinks(unknownSymbol).type = unknownType; | ||
globals[undefinedSymbol.name] = undefinedSymbol; | ||
globalScope.symbols[undefinedSymbol.name] = undefinedSymbol; | ||
// Initialize special types | ||
globalArrayType = <GenericType>getGlobalType("Array", /*arity*/ 1); | ||
globalObjectType = getGlobalType("Object"); | ||
|
@@ -14881,6 +14914,10 @@ namespace ts { | |
} | ||
|
||
anyArrayType = createArrayType(anyType); | ||
|
||
forEachValue(packages, package => { // Once all packages are merged and global scope is setup, merge global scope into each package | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move comment above |
||
mergeSymbolTable(package.symbols, globalScope.symbols); | ||
}); | ||
} | ||
|
||
function createInstantiatedPromiseLikeType(): ObjectType { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,10 +59,8 @@ namespace ts { | |
return { resolvedModule: { resolvedFileName }, failedLookupLocations }; | ||
} | ||
|
||
resolvedFileName = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ false, failedLookupLocations, host); | ||
return resolvedFileName | ||
? { resolvedModule: { resolvedFileName }, failedLookupLocations } | ||
: { resolvedModule: undefined, failedLookupLocations }; | ||
let res = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ false, failedLookupLocations, host, /*mustBePackage*/false); | ||
return { resolvedModule: res, failedLookupLocations }; | ||
} | ||
else { | ||
return loadModuleFromNodeModules(moduleName, containingDirectory, host); | ||
|
@@ -89,7 +87,7 @@ namespace ts { | |
} | ||
} | ||
|
||
function loadNodeModuleFromDirectory(candidate: string, loadOnlyDts: boolean, failedLookupLocation: string[], host: ModuleResolutionHost): string { | ||
function loadNodeModuleFromDirectory(candidate: string, loadOnlyDts: boolean, failedLookupLocation: string[], host: ModuleResolutionHost, mustBePackage: boolean): ResolvedModule { | ||
let packageJsonPath = combinePaths(candidate, "package.json"); | ||
if (host.fileExists(packageJsonPath)) { | ||
|
||
|
@@ -107,7 +105,7 @@ namespace ts { | |
if (jsonContent.typings) { | ||
let result = loadNodeModuleFromFile(normalizePath(combinePaths(candidate, jsonContent.typings)), loadOnlyDts, failedLookupLocation, host); | ||
if (result) { | ||
return result; | ||
return { resolvedFileName: result, packageRoot: packageJsonPath }; | ||
} | ||
} | ||
} | ||
|
@@ -116,7 +114,10 @@ namespace ts { | |
failedLookupLocation.push(packageJsonPath); | ||
} | ||
|
||
return loadNodeModuleFromFile(combinePaths(candidate, "index"), loadOnlyDts, failedLookupLocation, host); | ||
let result = loadNodeModuleFromFile(combinePaths(candidate, "index"), loadOnlyDts, failedLookupLocation, host); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (result) { | ||
return { resolvedFileName: result, packageRoot: mustBePackage ? result : undefined }; | ||
} | ||
} | ||
|
||
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { | ||
|
@@ -129,12 +130,12 @@ namespace ts { | |
let candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName)); | ||
let result = loadNodeModuleFromFile(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); | ||
if (result) { | ||
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; | ||
return { resolvedModule: { resolvedFileName: result, packageRoot: result }, failedLookupLocations }; | ||
} | ||
|
||
result = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ true, failedLookupLocations, host); | ||
if (result) { | ||
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations }; | ||
let res = loadNodeModuleFromDirectory(candidate, /* loadOnlyDts */ true, failedLookupLocations, host, /*mustBePackage*/ true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (res) { | ||
return { resolvedModule: res, failedLookupLocations }; | ||
} | ||
} | ||
|
||
|
@@ -474,7 +475,7 @@ namespace ts { | |
let resolutionChanged = oldResolution | ||
? !newResolution || | ||
oldResolution.resolvedFileName !== newResolution.resolvedFileName || | ||
!!oldResolution.isExternalLibraryImport !== !!newResolution.isExternalLibraryImport | ||
!!oldResolution.packageRoot !== !!newResolution.packageRoot | ||
: newResolution; | ||
|
||
if (resolutionChanged) { | ||
|
@@ -740,7 +741,7 @@ namespace ts { | |
diagnostic = Diagnostics.File_0_has_unsupported_extension_The_only_supported_extensions_are_1; | ||
diagnosticArgument = [fileName, "'" + supportedExtensions.join("', '") + "'"]; | ||
} | ||
else if (!findSourceFile(fileName, isDefaultLib, refFile, refPos, refEnd)) { | ||
else if (!findSourceFile(fileName, isDefaultLib, refFile, refPos, refEnd, refFile && refFile.package)) { | ||
diagnostic = Diagnostics.File_0_not_found; | ||
diagnosticArgument = [fileName]; | ||
} | ||
|
@@ -750,13 +751,13 @@ namespace ts { | |
} | ||
} | ||
else { | ||
let nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, isDefaultLib, refFile, refPos, refEnd); | ||
let nonTsFile: SourceFile = options.allowNonTsExtensions && findSourceFile(fileName, isDefaultLib, refFile, refPos, refEnd, refFile && refFile.package); | ||
if (!nonTsFile) { | ||
if (options.allowNonTsExtensions) { | ||
diagnostic = Diagnostics.File_0_not_found; | ||
diagnosticArgument = [fileName]; | ||
} | ||
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, isDefaultLib, refFile, refPos, refEnd))) { | ||
else if (!forEach(supportedExtensions, extension => findSourceFile(fileName + extension, isDefaultLib, refFile, refPos, refEnd, refFile && refFile.package))) { | ||
diagnostic = Diagnostics.File_0_not_found; | ||
fileName += ".ts"; | ||
diagnosticArgument = [fileName]; | ||
|
@@ -775,7 +776,7 @@ namespace ts { | |
} | ||
|
||
// Get source file from normalized fileName | ||
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { | ||
function findSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number, package?: PackageDescriptor): SourceFile { | ||
if (filesByName.contains(fileName)) { | ||
// We've already looked for this file, use cached result | ||
return getSourceFileFromCache(fileName, /*useAbsolutePath*/ false); | ||
|
@@ -800,9 +801,9 @@ namespace ts { | |
fileProcessingDiagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); | ||
} | ||
}); | ||
|
||
filesByName.set(fileName, file); | ||
if (file) { | ||
file.package = package; | ||
skipDefaultLib = skipDefaultLib || file.hasNoDefaultLib; | ||
|
||
// Set the source file for normalized absolute path | ||
|
@@ -848,6 +849,9 @@ namespace ts { | |
function processReferencedFiles(file: SourceFile, basePath: string) { | ||
forEach(file.referencedFiles, ref => { | ||
let referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName); | ||
if (file.package && !fileExtensionIs(ref.fileName, ".d.ts")) { | ||
fileProcessingDiagnostics.add(createFileDiagnostic(file, ref.pos, ref.end - ref.pos, Diagnostics.Exported_external_package_typings_file_cannot_contain_script_file_tripleslash_references_Please_contact_the_package_author_to_update_the_package_definition)); | ||
} | ||
processSourceFile(referencedFileName, /* isDefaultLib */ false, file, ref.pos, ref.end); | ||
}); | ||
} | ||
|
@@ -862,8 +866,8 @@ namespace ts { | |
let resolution = resolutions[i]; | ||
setResolvedModule(file, moduleNames[i], resolution); | ||
if (resolution && !options.noResolve) { | ||
const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i]); | ||
if (importedFile && resolution.isExternalLibraryImport) { | ||
const importedFile = findModuleSourceFile(resolution.resolvedFileName, file.imports[i], resolution.packageRoot ? {packageFile: resolution.packageRoot, symbols: {}} : file.package); | ||
if (importedFile && resolution.packageRoot) { | ||
if (!isExternalModule(importedFile)) { | ||
let start = getTokenPosOfNode(file.imports[i], file); | ||
fileProcessingDiagnostics.add(createFileDiagnostic(file, start, file.imports[i].end - start, Diagnostics.Exported_external_package_typings_file_0_is_not_a_module_Please_contact_the_package_author_to_update_the_package_definition, importedFile.fileName)); | ||
|
@@ -872,10 +876,6 @@ namespace 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)); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -886,8 +886,8 @@ namespace ts { | |
} | ||
return; | ||
|
||
function findModuleSourceFile(fileName: string, nameLiteral: Expression) { | ||
return findSourceFile(fileName, /* isDefaultLib */ false, file, skipTrivia(file.text, nameLiteral.pos), nameLiteral.end); | ||
function findModuleSourceFile(fileName: string, nameLiteral: Expression, package?: PackageDescriptor) { | ||
return findSourceFile(fileName, /* isDefaultLib */ false, file, skipTrivia(file.text, nameLiteral.pos), nameLiteral.end, package); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1233,6 +1233,10 @@ namespace ts { | |
isBracketed: boolean; | ||
} | ||
|
||
export interface PackageDescriptor extends Scope { | ||
packageFile: string; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a comment? Specifically mention that this can be a |
||
} | ||
|
||
// Source files are declarations when they are external modules. | ||
export interface SourceFile extends Declaration { | ||
statements: NodeArray<Statement>; | ||
|
@@ -1246,6 +1250,9 @@ namespace ts { | |
referencedFiles: FileReference[]; | ||
languageVariant: LanguageVariant; | ||
|
||
/* @internal */ | ||
package?: PackageDescriptor; | ||
|
||
// this map is used by transpiler to supply alternative names for dependencies (i.e. in case of bundling) | ||
/* @internal */ | ||
renamedDependencies?: Map<string>; | ||
|
@@ -1289,6 +1296,10 @@ namespace ts { | |
/* @internal */ imports: LiteralExpression[]; | ||
} | ||
|
||
export interface Scope { | ||
symbols: SymbolTable; // Locals associated with node (initialized by binding) | ||
} | ||
|
||
export interface ScriptReferenceHost { | ||
getCompilerOptions(): CompilerOptions; | ||
getSourceFile(fileName: string): SourceFile; | ||
|
@@ -1580,6 +1591,7 @@ namespace ts { | |
/* @internal */ | ||
export interface EmitResolver { | ||
hasGlobalName(name: string): boolean; | ||
hasPackageInternalName(node: SourceFile, name: string): boolean; | ||
getReferencedExportContainer(node: Identifier): SourceFile | ModuleDeclaration | EnumDeclaration; | ||
getReferencedImportDeclaration(node: Identifier): Declaration; | ||
getReferencedNestedRedeclaration(node: Identifier): Declaration; | ||
|
@@ -2308,9 +2320,8 @@ namespace ts { | |
* 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; | ||
packageRoot?: string; | ||
} | ||
|
||
export interface ResolvedModuleWithFailedLookupLocations { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get why it's nice to have this, but you don't actually end up using
globalScope
on its own - we always just grabsymbols
anyhow.