From 76b34939d088fda4bc7c05458c1d15006e27fc71 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 14 Jun 2016 16:24:01 -0700 Subject: [PATCH] Resolve external helpers module and report errors for missing module/exports --- src/compiler/checker.ts | 51 +++++++++++++++++++++++++++++--- src/compiler/factory.ts | 32 ++++++++++---------- src/compiler/program.ts | 11 +++++++ src/compiler/transformers/es6.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/ts.ts | 42 +++++++++++++------------- src/compiler/types.ts | 4 +-- src/compiler/utilities.ts | 2 ++ 8 files changed, 101 insertions(+), 45 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 130e4f5d653..bd3d87ac3ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1264,10 +1264,13 @@ namespace ts { } const moduleReferenceLiteral = moduleReferenceExpression; + return resolveExternalModule(location, moduleReferenceLiteral.text, moduleNotFoundError, moduleReferenceLiteral); + } + function resolveExternalModule(location: Node, moduleReference: string, moduleNotFoundError: DiagnosticMessage, errorNode: Node): Symbol { // Module names are escaped in our symbol table. However, string literal values aren't. // Escape the name in the "require(...)" clause to ensure we find the right symbol. - const moduleName = escapeIdentifier(moduleReferenceLiteral.text); + const moduleName = escapeIdentifier(moduleReference); if (moduleName === undefined) { return; @@ -1282,7 +1285,7 @@ namespace ts { } } - const resolvedModule = getResolvedModule(getSourceFileOfNode(location), moduleReferenceLiteral.text); + const resolvedModule = getResolvedModule(getSourceFileOfNode(location), moduleReference); const sourceFile = resolvedModule && host.getSourceFile(resolvedModule.resolvedFileName); if (sourceFile) { if (sourceFile.symbol) { @@ -1291,7 +1294,7 @@ namespace ts { } if (moduleNotFoundError) { // report errors only if it was requested - error(moduleReferenceLiteral, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); + error(errorNode, Diagnostics.File_0_is_not_a_module, sourceFile.fileName); } return undefined; } @@ -1305,7 +1308,7 @@ namespace ts { if (moduleNotFoundError) { // report errors only if it was requested - error(moduleReferenceLiteral, moduleNotFoundError, moduleName); + error(errorNode, moduleNotFoundError, moduleName); } return undefined; } @@ -17843,6 +17846,46 @@ namespace ts { const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined); globalReadonlyArrayType = symbol && getTypeOfGlobalSymbol(symbol, /*arity*/ 1); anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType; + + // If we have specified that we are importing helpers, we should report global + // errors if we cannot resolve the helpers external module, or if it does not have + // the necessary helpers exported. + if (compilerOptions.importHelpers) { + forEach(host.getSourceFiles(), sourceFile => { + const requestedHelpers = sourceFile.flags & NodeFlags.EmitHelperFlags; + if (requestedHelpers && (compilerOptions.isolatedModules || isExternalModule(sourceFile))) { + const helpers = resolveExternalModule(sourceFile, externalHelpersModuleNameText, Diagnostics.Cannot_find_module_0, /*errorNode*/ undefined); + if (helpers) { + const exports = helpers.exports; + if (requestedHelpers & NodeFlags.HasClassExtends && languageVersion < ScriptTarget.ES6) { + verifyHelperSymbol(exports, "__extends", SymbolFlags.Function); + } + if (requestedHelpers & NodeFlags.HasJsxSpreadAttributes && compilerOptions.jsx !== JsxEmit.Preserve) { + verifyHelperSymbol(exports, "__assign", SymbolFlags.Value); + } + if (requestedHelpers & NodeFlags.HasDecorators) { + verifyHelperSymbol(exports, "__decorate", SymbolFlags.Value); + if (compilerOptions.emitDecoratorMetadata) { + verifyHelperSymbol(exports, "__metadata", SymbolFlags.Value); + } + } + if (requestedHelpers & NodeFlags.HasParamDecorators) { + verifyHelperSymbol(exports, "__param", SymbolFlags.Value); + } + if (requestedHelpers & NodeFlags.HasAsyncFunctions) { + verifyHelperSymbol(exports, "__awaiter", SymbolFlags.Value); + } + } + } + }); + } + } + + function verifyHelperSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags) { + const symbol = getSymbol(symbols, escapeIdentifier(name), meaning); + if (!symbol) { + error(/*location*/ undefined, Diagnostics.Module_0_has_no_exported_member_1, externalHelpersModuleNameText, name); + } } function createInstantiatedPromiseLikeType(): ObjectType { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index b0873c29d3a..f879f3660e4 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -824,7 +824,7 @@ namespace ts { if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames; if (node.imports !== undefined) updated.imports = node.imports; if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations; - if (node.tslib !== undefined) updated.tslib = node.tslib; + if (node.externalHelpersModuleName !== undefined) updated.externalHelpersModuleName = node.externalHelpersModuleName; return updateNode(updated, node); } @@ -1065,15 +1065,15 @@ namespace ts { // Helpers - export function createHelperName(tslib: Identifier | undefined, name: string) { - return tslib - ? createPropertyAccess(tslib, name) + export function createHelperName(externalHelpersModuleName: Identifier | undefined, name: string) { + return externalHelpersModuleName + ? createPropertyAccess(externalHelpersModuleName, name) : createIdentifier(name); } - export function createExtendsHelper(tslib: Identifier | undefined, name: Identifier) { + export function createExtendsHelper(externalHelpersModuleName: Identifier | undefined, name: Identifier) { return createCall( - createHelperName(tslib, "__extends"), + createHelperName(externalHelpersModuleName, "__extends"), /*typeArguments*/ undefined, [ name, @@ -1082,17 +1082,17 @@ namespace ts { ); } - export function createAssignHelper(tslib: Identifier | undefined, attributesSegments: Expression[]) { + export function createAssignHelper(externalHelpersModuleName: Identifier | undefined, attributesSegments: Expression[]) { return createCall( - createHelperName(tslib, "__assign"), + createHelperName(externalHelpersModuleName, "__assign"), /*typeArguments*/ undefined, attributesSegments ); } - export function createParamHelper(tslib: Identifier | undefined, expression: Expression, parameterOffset: number, location?: TextRange) { + export function createParamHelper(externalHelpersModuleName: Identifier | undefined, expression: Expression, parameterOffset: number, location?: TextRange) { return createCall( - createHelperName(tslib, "__param"), + createHelperName(externalHelpersModuleName, "__param"), /*typeArguments*/ undefined, [ createLiteral(parameterOffset), @@ -1102,9 +1102,9 @@ namespace ts { ); } - export function createMetadataHelper(tslib: Identifier | undefined, metadataKey: string, metadataValue: Expression) { + export function createMetadataHelper(externalHelpersModuleName: Identifier | undefined, metadataKey: string, metadataValue: Expression) { return createCall( - createHelperName(tslib, "__metadata"), + createHelperName(externalHelpersModuleName, "__metadata"), /*typeArguments*/ undefined, [ createLiteral(metadataKey), @@ -1113,7 +1113,7 @@ namespace ts { ); } - export function createDecorateHelper(tslib: Identifier | undefined, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { + export function createDecorateHelper(externalHelpersModuleName: Identifier | undefined, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) { const argumentsArray: Expression[] = []; argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true)); argumentsArray.push(target); @@ -1124,12 +1124,12 @@ namespace ts { } } - return createCall(createHelperName(tslib, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); + return createCall(createHelperName(externalHelpersModuleName, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location); } - export function createAwaiterHelper(tslib: Identifier | undefined, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { + export function createAwaiterHelper(externalHelpersModuleName: Identifier | undefined, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) { return createCall( - createHelperName(tslib, "__awaiter"), + createHelperName(externalHelpersModuleName, "__awaiter"), /*typeArguments*/ undefined, [ createThis(), diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 420fe058a26..e6f809995c0 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1723,6 +1723,17 @@ namespace ts { let imports: LiteralExpression[]; let moduleAugmentations: LiteralExpression[]; + // If we are importing helpers, we need to add a synthetic reference to resolve the + // helpers library. + if (options.importHelpers + && (options.isolatedModules || isExternalModuleFile) + && !file.isDeclarationFile) { + const externalHelpersModuleReference = createNode(SyntaxKind.StringLiteral); + externalHelpersModuleReference.text = externalHelpersModuleNameText; + externalHelpersModuleReference.parent = file; + imports = [externalHelpersModuleReference]; + } + for (const node of file.statements) { collectModuleReferences(node, /*inAmbientModule*/ false); if (isJavaScriptFile) { diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index cd8878b8a71..060ad043e78 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -702,7 +702,7 @@ namespace ts { if (extendsClauseElement) { statements.push( createStatement( - createExtendsHelper(currentSourceFile.tslib, getDeclarationName(node)), + createExtendsHelper(currentSourceFile.externalHelpersModuleName, getDeclarationName(node)), /*location*/ extendsClauseElement ) ); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 969b1bf0904..90503d5a5f6 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -106,7 +106,7 @@ namespace ts { // Either emit one big object literal (no spread attribs), or // a call to the __assign helper. objectProperties = singleOrUndefined(segments) - || createAssignHelper(currentSourceFile.tslib, segments); + || createAssignHelper(currentSourceFile.externalHelpersModuleName, segments); } const element = createReactCreateElement( diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 5b6f375616d..8dd3630cd19 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -51,7 +51,7 @@ namespace ts { let currentNamespace: ModuleDeclaration; let currentNamespaceContainerName: Identifier; let currentScope: SourceFile | Block | ModuleBlock | CaseBlock; - let currentSourceFileTslib: Identifier; + let currentSourceFileExternalHelpersModuleName: Identifier; /** * Keeps track of whether expression substitution has been enabled for specific edge cases. @@ -432,22 +432,22 @@ namespace ts { startLexicalEnvironment(); const statements: Statement[] = []; const statementOffset = addPrologueDirectives(statements, node.statements); - const tslib = createUniqueName("tslib"); - const tslibImport = createImportDeclaration( - createImportClause(/*name*/ undefined, createNamespaceImport(tslib)), - createLiteral("tslib") + const externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText); + const externalHelpersModuleImport = createImportDeclaration( + createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)), + createLiteral(externalHelpersModuleNameText) ); - tslibImport.parent = node; - tslibImport.flags &= ~NodeFlags.Synthesized; - statements.push(tslibImport); + externalHelpersModuleImport.parent = node; + externalHelpersModuleImport.flags &= ~NodeFlags.Synthesized; + statements.push(externalHelpersModuleImport); - currentSourceFileTslib = tslib; + currentSourceFileExternalHelpersModuleName = externalHelpersModuleName; addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset)); addRange(statements, endLexicalEnvironment()); - currentSourceFileTslib = undefined; + currentSourceFileExternalHelpersModuleName = undefined; node = updateSourceFileNode(node, createNodeArray(statements, node.statements)); - node.tslib = tslib; + node.externalHelpersModuleName = externalHelpersModuleName; } else { node = visitEachChild(node, visitor, context); @@ -1382,7 +1382,7 @@ namespace ts { : undefined; const helper = createDecorateHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, decoratorExpressions, prefix, memberName, @@ -1432,7 +1432,7 @@ namespace ts { const expression = createAssignment( decoratedClassAlias, createDecorateHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, decoratorExpressions, getDeclarationName(node) ) @@ -1456,7 +1456,7 @@ namespace ts { const result = createAssignment( getDeclarationName(node), createDecorateHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, decoratorExpressions, getDeclarationName(node) ), @@ -1489,7 +1489,7 @@ namespace ts { expressions = []; for (const decorator of decorators) { const helper = createParamHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, transformDecorator(decorator), parameterOffset, /*location*/ decorator.expression); @@ -1519,13 +1519,13 @@ namespace ts { function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) { if (compilerOptions.emitDecoratorMetadata) { if (shouldAddTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentSourceFileTslib, "design:type", serializeTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(currentSourceFileExternalHelpersModuleName, "design:type", serializeTypeOfNode(node))); } if (shouldAddParamTypesMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentSourceFileTslib, "design:paramtypes", serializeParameterTypesOfNode(node))); + decoratorExpressions.push(createMetadataHelper(currentSourceFileExternalHelpersModuleName, "design:paramtypes", serializeParameterTypesOfNode(node))); } if (shouldAddReturnTypeMetadata(node)) { - decoratorExpressions.push(createMetadataHelper(currentSourceFileTslib, "design:returntype", serializeReturnTypeOfNode(node))); + decoratorExpressions.push(createMetadataHelper(currentSourceFileExternalHelpersModuleName, "design:returntype", serializeReturnTypeOfNode(node))); } } } @@ -1543,7 +1543,7 @@ namespace ts { (properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, /*equalsGreaterThanToken*/ undefined, serializeReturnTypeOfNode(node)))); } if (properties) { - decoratorExpressions.push(createMetadataHelper(currentSourceFileTslib, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); + decoratorExpressions.push(createMetadataHelper(currentSourceFileExternalHelpersModuleName, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true))); } } } @@ -2234,7 +2234,7 @@ namespace ts { statements.push( createReturn( createAwaiterHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, hasLexicalArguments, promiseConstructor, transformFunctionBodyWorker(node.body) @@ -2261,7 +2261,7 @@ namespace ts { } else { return createAwaiterHelper( - currentSourceFileTslib, + currentSourceFileExternalHelpersModuleName, hasLexicalArguments, promiseConstructor, transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4e191ae0170..4d4d2e6eb70 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1726,8 +1726,8 @@ namespace ts { /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; - // The synthesized identifier for an imported tslib helpers runtime. - /* @internal */ tslib?: Identifier; + // The synthesized identifier for an imported external helpers module. + /* @internal */ externalHelpersModuleName?: Identifier; } export interface ScriptReferenceHost { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b53a5ddc51f..975fd6dd939 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2,6 +2,8 @@ /* @internal */ namespace ts { + export const externalHelpersModuleNameText = "tslib"; + export interface ReferencePathMatchResult { fileReference?: FileReference; diagnosticMessage?: DiagnosticMessage;