mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-24 11:43:18 -05:00
Rewrite named imports to reference properties on module instance
This commit is contained in:
@@ -10141,62 +10141,149 @@ module ts {
|
||||
function isUniqueLocalName(name: string, container: Node): boolean {
|
||||
for (var node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
|
||||
if (node.locals && hasProperty(node.locals, name)) {
|
||||
var symbolWithRelevantName = node.locals[name];
|
||||
if (symbolWithRelevantName.flags & (SymbolFlags.Value | SymbolFlags.ExportValue)) {
|
||||
// We conservatively include import symbols to cover cases where they're emitted as locals
|
||||
if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Import)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// An import can be emitted too, if it is referenced as a value.
|
||||
// Make sure the name in question does not collide with an import.
|
||||
if (symbolWithRelevantName.flags & SymbolFlags.Import) {
|
||||
var importEqualsDeclarationWithRelevantName = <ImportEqualsDeclaration>getDeclarationOfKind(symbolWithRelevantName, SyntaxKind.ImportEqualsDeclaration);
|
||||
if (isReferencedImportDeclaration(importEqualsDeclarationWithRelevantName)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string {
|
||||
var links = getNodeLinks(container);
|
||||
if (!links.localModuleName) {
|
||||
var prefix = "";
|
||||
var name = unescapeIdentifier(container.name.text);
|
||||
while (!isUniqueLocalName(escapeIdentifier(prefix + name), container)) {
|
||||
prefix += "_";
|
||||
}
|
||||
links.localModuleName = prefix + getTextOfNode(container.name);
|
||||
function getGeneratedNamesForSourceFile(sourceFile: SourceFile): Map<string> {
|
||||
var links = getNodeLinks(sourceFile);
|
||||
var generatedNames = links.generatedNames;
|
||||
if (!generatedNames) {
|
||||
generatedNames = links.generatedNames = {};
|
||||
generateNames(sourceFile);
|
||||
}
|
||||
return generatedNames;
|
||||
|
||||
function generateNames(node: Node) {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModuleDeclaration:
|
||||
generateNameForModuleOrEnum(<ModuleDeclaration>node);
|
||||
generateNames((<ModuleDeclaration>node).body);
|
||||
break;
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
generateNameForModuleOrEnum(<EnumDeclaration>node);
|
||||
break;
|
||||
case SyntaxKind.ImportDeclaration:
|
||||
generateNameForImportDeclaration(<ImportDeclaration>node);
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
case SyntaxKind.ModuleBlock:
|
||||
forEach((<SourceFile | ModuleBlock>node).statements, generateNames);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function isExistingName(name: string) {
|
||||
return hasProperty(sourceFile.identifiers, name) || hasProperty(generatedNames, name);
|
||||
}
|
||||
|
||||
function makeUniqueName(baseName: string): string {
|
||||
// First try '_name'
|
||||
if (baseName.charCodeAt(0) !== CharacterCodes._) {
|
||||
var baseName = "_" + baseName;
|
||||
if (!isExistingName(baseName)) {
|
||||
return baseName;
|
||||
}
|
||||
}
|
||||
// Find the first unique '_name_n', where n is a positive number
|
||||
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
|
||||
baseName += "_";
|
||||
}
|
||||
var i = 1;
|
||||
while (true) {
|
||||
name = baseName + i;
|
||||
if (!isExistingName(name)) {
|
||||
return name;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function assignGeneratedName(node: Node, name: string) {
|
||||
generatedNames[name] = name;
|
||||
getNodeLinks(node).generatedName = unescapeIdentifier(name);
|
||||
}
|
||||
|
||||
function generateNameForModuleOrEnum(node: ModuleDeclaration | EnumDeclaration) {
|
||||
if (node.name.kind === SyntaxKind.Identifier) {
|
||||
var name = node.name.text;
|
||||
// Use module/enum name itself if it is unique, otherwise make a unique variation
|
||||
assignGeneratedName(node, isUniqueLocalName(name, node) ? name : makeUniqueName(name));
|
||||
}
|
||||
}
|
||||
|
||||
function generateNameForImportDeclaration(node: ImportDeclaration) {
|
||||
if (node.importClause && node.importClause.namedBindings && node.importClause.namedBindings.kind === SyntaxKind.NamedImports) {
|
||||
var expr = getImportedModuleName(node);
|
||||
var baseName = expr.kind === SyntaxKind.StringLiteral ?
|
||||
escapeIdentifier(makeIdentifierFromModuleName((<LiteralExpression>expr).text)) : "module";
|
||||
assignGeneratedName(node, makeUniqueName(baseName));
|
||||
}
|
||||
}
|
||||
return links.localModuleName;
|
||||
}
|
||||
|
||||
function getLocalNameForSymbol(symbol: Symbol, location: Node): string {
|
||||
function getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration) {
|
||||
var links = getNodeLinks(node);
|
||||
if (!links.generatedName) {
|
||||
getGeneratedNamesForSourceFile(getSourceFile(node));
|
||||
}
|
||||
return links.generatedName;
|
||||
}
|
||||
|
||||
function getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string {
|
||||
return getGeneratedNameForNode(container);
|
||||
}
|
||||
|
||||
function getLocalNameForImportDeclaration(node: ImportDeclaration): string {
|
||||
return getGeneratedNameForNode(node);
|
||||
}
|
||||
|
||||
function getImportNameSubstitution(symbol: Symbol): string {
|
||||
var declaration = getDeclarationOfImportSymbol(symbol);
|
||||
if (declaration && declaration.kind === SyntaxKind.ImportSpecifier) {
|
||||
var moduleName = getGeneratedNameForNode(<ImportDeclaration>declaration.parent.parent.parent);
|
||||
var propertyName = (<ImportSpecifier>declaration).propertyName || (<ImportSpecifier>declaration).name;
|
||||
return moduleName + "." + unescapeIdentifier(propertyName.text);
|
||||
}
|
||||
}
|
||||
|
||||
function getExportNameSubstitution(symbol: Symbol, location: Node): string {
|
||||
if (isExternalModuleSymbol(symbol.parent)) {
|
||||
return "exports." + unescapeIdentifier(symbol.name);
|
||||
}
|
||||
var node = location;
|
||||
var containerSymbol = getParentOfSymbol(symbol);
|
||||
while (node) {
|
||||
if ((node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(node) === symbol) {
|
||||
return getLocalNameOfContainer(<ModuleDeclaration | EnumDeclaration>node);
|
||||
if ((node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration) && getSymbolOfNode(node) === containerSymbol) {
|
||||
return getGeneratedNameForNode(<ModuleDeclaration | EnumDeclaration>node) + "." + unescapeIdentifier(symbol.name);
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
Debug.fail("getLocalNameForSymbol failed");
|
||||
}
|
||||
|
||||
function getExpressionNamePrefix(node: Identifier): string {
|
||||
function getExpressionNameSubstitution(node: Identifier): string {
|
||||
var symbol = getNodeLinks(node).resolvedSymbol;
|
||||
if (symbol) {
|
||||
// In general, we need to prefix an identifier with its parent name if it references
|
||||
// an exported entity from another module declaration. If we reference an exported
|
||||
// entity within the same module declaration, then whether we prefix depends on the
|
||||
// kind of entity. SymbolFlags.ExportHasLocal encompasses all the kinds that we
|
||||
// do NOT prefix.
|
||||
// Whan an identifier resolves to a parented symbol, it references an exported entity from
|
||||
// another declaration of the same internal module.
|
||||
if (symbol.parent) {
|
||||
return getExportNameSubstitution(symbol, node.parent);
|
||||
}
|
||||
// If we reference an exported entity within the same module declaration, then whether
|
||||
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
|
||||
// kinds that we do NOT prefix.
|
||||
var exportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
|
||||
if (symbol !== exportSymbol && !(exportSymbol.flags & SymbolFlags.ExportHasLocal)) {
|
||||
symbol = exportSymbol;
|
||||
return getExportNameSubstitution(exportSymbol, node.parent);
|
||||
}
|
||||
if (symbol.parent) {
|
||||
return isExternalModuleSymbol(symbol.parent) ? "exports" : getLocalNameForSymbol(getParentOfSymbol(symbol), node.parent);
|
||||
// Named imports from ES6 import declarations are rewritten
|
||||
if (symbol.flags & SymbolFlags.Import) {
|
||||
return getImportNameSubstitution(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10302,13 +10389,14 @@ module ts {
|
||||
}
|
||||
|
||||
function isUnknownIdentifier(location: Node, name: string): boolean {
|
||||
return !resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
return !resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined) &&
|
||||
!hasProperty(getGeneratedNamesForSourceFile(getSourceFile(location)), name);
|
||||
}
|
||||
|
||||
function createResolver(): EmitResolver {
|
||||
return {
|
||||
getLocalNameOfContainer,
|
||||
getExpressionNamePrefix,
|
||||
getGeneratedNameForNode,
|
||||
getExpressionNameSubstitution,
|
||||
getExportAssignmentName,
|
||||
isReferencedImportDeclaration,
|
||||
getNodeCheckFlags,
|
||||
|
||||
@@ -20,7 +20,6 @@ module ts {
|
||||
importNode: ImportDeclaration | ImportEqualsDeclaration;
|
||||
declarationNode?: ImportEqualsDeclaration | ImportClause | NamespaceImport;
|
||||
namedImports?: NamedImports;
|
||||
tempName?: Identifier; // Temporary name for module instance
|
||||
}
|
||||
|
||||
interface SymbolAccessibilityDiagnostic {
|
||||
@@ -2338,12 +2337,13 @@ module ts {
|
||||
}
|
||||
|
||||
function emitExpressionIdentifier(node: Identifier) {
|
||||
var prefix = resolver.getExpressionNamePrefix(node);
|
||||
if (prefix) {
|
||||
write(prefix);
|
||||
write(".");
|
||||
var substitution = resolver.getExpressionNameSubstitution(node);
|
||||
if (substitution) {
|
||||
write(substitution);
|
||||
}
|
||||
else {
|
||||
writeTextOfNode(currentSourceFile, node);
|
||||
}
|
||||
writeTextOfNode(currentSourceFile, node);
|
||||
}
|
||||
|
||||
function emitIdentifier(node: Identifier) {
|
||||
@@ -2526,7 +2526,7 @@ module ts {
|
||||
// export var obj = { y };
|
||||
// }
|
||||
// The short-hand property in obj need to emit as such ... = { y : m.y } regardless of the TargetScript version
|
||||
if (languageVersion < ScriptTarget.ES6 || resolver.getExpressionNamePrefix(node.name)) {
|
||||
if (languageVersion < ScriptTarget.ES6 || resolver.getExpressionNameSubstitution(node.name)) {
|
||||
// Emit identifier as an identifier
|
||||
write(": ");
|
||||
// Even though this is stored as identifier treat it as an expression
|
||||
@@ -2964,7 +2964,7 @@ module ts {
|
||||
emitStart(node.name);
|
||||
if (getCombinedNodeFlags(node) & NodeFlags.Export) {
|
||||
var container = getContainingModule(node);
|
||||
write(container ? resolver.getLocalNameOfContainer(container) : "exports");
|
||||
write(container ? resolver.getGeneratedNameForNode(container) : "exports");
|
||||
write(".");
|
||||
}
|
||||
emitNode(node.name);
|
||||
@@ -3771,7 +3771,7 @@ module ts {
|
||||
emitStart(node);
|
||||
write("(function (");
|
||||
emitStart(node.name);
|
||||
write(resolver.getLocalNameOfContainer(node));
|
||||
write(resolver.getGeneratedNameForNode(node));
|
||||
emitEnd(node.name);
|
||||
write(") {");
|
||||
increaseIndent();
|
||||
@@ -3802,9 +3802,9 @@ module ts {
|
||||
function emitEnumMember(node: EnumMember) {
|
||||
var enumParent = <EnumDeclaration>node.parent;
|
||||
emitStart(node);
|
||||
write(resolver.getLocalNameOfContainer(enumParent));
|
||||
write(resolver.getGeneratedNameForNode(enumParent));
|
||||
write("[");
|
||||
write(resolver.getLocalNameOfContainer(enumParent));
|
||||
write(resolver.getGeneratedNameForNode(enumParent));
|
||||
write("[");
|
||||
emitExpressionForPropertyName(node.name);
|
||||
write("] = ");
|
||||
@@ -3860,7 +3860,7 @@ module ts {
|
||||
emitStart(node);
|
||||
write("(function (");
|
||||
emitStart(node.name);
|
||||
write(resolver.getLocalNameOfContainer(node));
|
||||
write(resolver.getGeneratedNameForNode(node));
|
||||
emitEnd(node.name);
|
||||
write(") ");
|
||||
if (node.body.kind === SyntaxKind.ModuleBlock) {
|
||||
@@ -3918,23 +3918,6 @@ module ts {
|
||||
emitRequire(moduleName);
|
||||
}
|
||||
|
||||
function emitNamedImportAssignments(namedImports: NamedImports, moduleReference: Identifier) {
|
||||
var elements = namedImports.elements;
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i];
|
||||
if (resolver.isReferencedImportDeclaration(element)) {
|
||||
writeLine();
|
||||
if (!(element.flags & NodeFlags.Export)) write("var ");
|
||||
emitModuleMemberName(element);
|
||||
write(" = ");
|
||||
emit(moduleReference);
|
||||
write(".");
|
||||
emit(element.propertyName || element.name);
|
||||
write(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitImportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration) {
|
||||
var info = getExternalImportInfo(node);
|
||||
if (info) {
|
||||
@@ -3943,7 +3926,7 @@ module ts {
|
||||
if (compilerOptions.module !== ModuleKind.AMD) {
|
||||
emitLeadingComments(node);
|
||||
emitStart(node);
|
||||
var moduleName = getImportedModuleName(info.importNode);
|
||||
var moduleName = getImportedModuleName(node);
|
||||
if (declarationNode) {
|
||||
if (!(declarationNode.flags & NodeFlags.Export)) write("var ");
|
||||
emitModuleMemberName(declarationNode);
|
||||
@@ -3952,10 +3935,9 @@ module ts {
|
||||
}
|
||||
else if (namedImports) {
|
||||
write("var ");
|
||||
emit(info.tempName);
|
||||
write(resolver.getGeneratedNameForNode(<ImportDeclaration>node));
|
||||
write(" = ");
|
||||
emitRequire(moduleName);
|
||||
emitNamedImportAssignments(namedImports, info.tempName);
|
||||
}
|
||||
else {
|
||||
emitRequire(moduleName);
|
||||
@@ -3972,9 +3954,6 @@ module ts {
|
||||
write(";");
|
||||
}
|
||||
}
|
||||
else if (namedImports) {
|
||||
emitNamedImportAssignments(namedImports, info.tempName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4027,7 +4006,8 @@ module ts {
|
||||
}
|
||||
return {
|
||||
importNode: <ImportDeclaration>node,
|
||||
namedImports: <NamedImports>importClause.namedBindings
|
||||
namedImports: <NamedImports>importClause.namedBindings,
|
||||
localName: resolver.getGeneratedNameForNode(<ImportDeclaration>node)
|
||||
};
|
||||
}
|
||||
return {
|
||||
@@ -4042,9 +4022,6 @@ module ts {
|
||||
var info = createExternalImportInfo(node);
|
||||
if (info) {
|
||||
if ((!info.declarationNode && !info.namedImports) || resolver.isReferencedImportDeclaration(node)) {
|
||||
if (!info.declarationNode) {
|
||||
info.tempName = createTempVariable(sourceFile);
|
||||
}
|
||||
externalImports.push(info);
|
||||
}
|
||||
}
|
||||
@@ -4095,7 +4072,12 @@ module ts {
|
||||
write("], function (require, exports");
|
||||
forEach(externalImports, info => {
|
||||
write(", ");
|
||||
emit(info.declarationNode ? info.declarationNode.name : info.tempName);
|
||||
if (info.declarationNode) {
|
||||
emit(info.declarationNode.name);
|
||||
}
|
||||
else {
|
||||
write(resolver.getGeneratedNameForNode(<ImportDeclaration>info.importNode));
|
||||
}
|
||||
});
|
||||
write(") {");
|
||||
increaseIndent();
|
||||
|
||||
@@ -1163,8 +1163,8 @@ module ts {
|
||||
}
|
||||
|
||||
export interface EmitResolver {
|
||||
getLocalNameOfContainer(container: ModuleDeclaration | EnumDeclaration): string;
|
||||
getExpressionNamePrefix(node: Identifier): string;
|
||||
getGeneratedNameForNode(node: ModuleDeclaration | EnumDeclaration | ImportDeclaration): string;
|
||||
getExpressionNameSubstitution(node: Identifier): string;
|
||||
getExportAssignmentName(node: SourceFile): string;
|
||||
isReferencedImportDeclaration(node: Node): boolean;
|
||||
isTopLevelValueImportEqualsWithEntityName(node: ImportEqualsDeclaration): boolean;
|
||||
@@ -1312,7 +1312,8 @@ module ts {
|
||||
enumMemberValue?: number; // Constant value of enum member
|
||||
isIllegalTypeReferenceInConstraint?: boolean; // Is type reference in constraint refers to the type parameter from the same list
|
||||
isVisible?: boolean; // Is this node visible
|
||||
localModuleName?: string; // Local name for module instance
|
||||
generatedName?: string; // Generated name for module, enum, or import declaration
|
||||
generatedNames?: Map<string>; // Generated names table for source file
|
||||
assignmentChecks?: Map<boolean>; // Cache of assignment checks
|
||||
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
|
||||
importOnRightSide?: Symbol; // for import declarations - import that appear on the right side
|
||||
|
||||
@@ -181,6 +181,12 @@ module ts {
|
||||
return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier;
|
||||
}
|
||||
|
||||
// Make an identifier from an external module name by extracting the string after the last "/" and replacing
|
||||
// all non-alphanumeric characters with underscores
|
||||
export function makeIdentifierFromModuleName(moduleName: string): string {
|
||||
return getBaseFileName(moduleName).replace(/\W/g, "_");
|
||||
}
|
||||
|
||||
// Return display name of an identifier
|
||||
// Computed property names will just be emitted as "[<expr>]", where <expr> is the source
|
||||
// text of the expression in the computed property.
|
||||
|
||||
Reference in New Issue
Block a user