UNPKG

@angular/tsc-wrapped

Version:
703 lines 34.8 kB
"use strict"; /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ Object.defineProperty(exports, "__esModule", { value: true }); var ts = require("typescript"); var evaluator_1 = require("./evaluator"); var schema_1 = require("./schema"); var symbols_1 = require("./symbols"); // In TypeScript 2.1 these flags moved // These helpers work for both 2.0 and 2.1. var isExport = ts.ModifierFlags ? (function (node) { return !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export); }) : (function (node) { return !!((node.flags & ts.NodeFlags.Export)); }); var isStatic = ts.ModifierFlags ? (function (node) { return !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static); }) : (function (node) { return !!((node.flags & ts.NodeFlags.Static)); }); /** * A set of collector options to use when collecting metadata. */ var CollectorOptions = (function () { function CollectorOptions() { } return CollectorOptions; }()); exports.CollectorOptions = CollectorOptions; /** * Collect decorator metadata from a TypeScript module. */ var MetadataCollector = (function () { function MetadataCollector(options) { if (options === void 0) { options = {}; } this.options = options; } /** * Returns a JSON.stringify friendly form describing the decorators of the exported classes from * the source file that is expected to correspond to a module. */ MetadataCollector.prototype.getMetadata = function (sourceFile, strict) { if (strict === void 0) { strict = false; } var locals = new symbols_1.Symbols(sourceFile); var nodeMap = new Map(); var evaluator = new evaluator_1.Evaluator(locals, nodeMap, this.options); var metadata; var exports; function objFromDecorator(decoratorNode) { return evaluator.evaluateNode(decoratorNode.expression); } function recordEntry(entry, node) { nodeMap.set(entry, node); return entry; } function errorSym(message, node, context) { return evaluator_1.errorSymbol(message, node, context, sourceFile); } function maybeGetSimpleFunction(functionDeclaration) { if (functionDeclaration.name.kind == ts.SyntaxKind.Identifier) { var nameNode = functionDeclaration.name; var functionName = nameNode.text; var functionBody = functionDeclaration.body; if (functionBody && functionBody.statements.length == 1) { var statement = functionBody.statements[0]; if (statement.kind === ts.SyntaxKind.ReturnStatement) { var returnStatement = statement; if (returnStatement.expression) { var func = { __symbolic: 'function', parameters: namesOf(functionDeclaration.parameters), value: evaluator.evaluateNode(returnStatement.expression) }; if (functionDeclaration.parameters.some(function (p) { return p.initializer != null; })) { func.defaults = functionDeclaration.parameters.map(function (p) { return p.initializer && evaluator.evaluateNode(p.initializer); }); } return recordEntry({ func: func, name: functionName }, functionDeclaration); } } } } } function classMetadataOf(classDeclaration) { var result = { __symbolic: 'class' }; function getDecorators(decorators) { if (decorators && decorators.length) return decorators.map(function (decorator) { return objFromDecorator(decorator); }); return undefined; } function referenceFrom(node) { var result = evaluator.evaluateNode(node); if (schema_1.isMetadataError(result) || schema_1.isMetadataSymbolicReferenceExpression(result) || schema_1.isMetadataSymbolicSelectExpression(result)) { return result; } else { return errorSym('Symbol reference expected', node); } } // Add class parents if (classDeclaration.heritageClauses) { classDeclaration.heritageClauses.forEach(function (hc) { if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) { hc.types.forEach(function (type) { return result.extends = referenceFrom(type.expression); }); } }); } // Add arity if the type is generic var typeParameters = classDeclaration.typeParameters; if (typeParameters && typeParameters.length) { result.arity = typeParameters.length; } // Add class decorators if (classDeclaration.decorators) { result.decorators = getDecorators(classDeclaration.decorators); } // member decorators var members = null; function recordMember(name, metadata) { if (!members) members = {}; var data = members.hasOwnProperty(name) ? members[name] : []; data.push(metadata); members[name] = data; } // static member var statics = null; function recordStaticMember(name, value) { if (!statics) statics = {}; statics[name] = value; } for (var _i = 0, _a = classDeclaration.members; _i < _a.length; _i++) { var member = _a[_i]; var isConstructor = false; switch (member.kind) { case ts.SyntaxKind.Constructor: case ts.SyntaxKind.MethodDeclaration: isConstructor = member.kind === ts.SyntaxKind.Constructor; var method = member; if (isStatic(method)) { var maybeFunc = maybeGetSimpleFunction(method); if (maybeFunc) { recordStaticMember(maybeFunc.name, maybeFunc.func); } continue; } var methodDecorators = getDecorators(method.decorators); var parameters = method.parameters; var parameterDecoratorData = []; var parametersData = []; var hasDecoratorData = false; var hasParameterData = false; for (var _b = 0, parameters_1 = parameters; _b < parameters_1.length; _b++) { var parameter = parameters_1[_b]; var parameterData = getDecorators(parameter.decorators); parameterDecoratorData.push(parameterData); hasDecoratorData = hasDecoratorData || !!parameterData; if (isConstructor) { if (parameter.type) { parametersData.push(referenceFrom(parameter.type)); } else { parametersData.push(null); } hasParameterData = true; } } var data = { __symbolic: isConstructor ? 'constructor' : 'method' }; var name_1 = isConstructor ? '__ctor__' : evaluator.nameOf(member.name); if (methodDecorators) { data.decorators = methodDecorators; } if (hasDecoratorData) { data.parameterDecorators = parameterDecoratorData; } if (hasParameterData) { data.parameters = parametersData; } if (!schema_1.isMetadataError(name_1)) { recordMember(name_1, data); } break; case ts.SyntaxKind.PropertyDeclaration: case ts.SyntaxKind.GetAccessor: case ts.SyntaxKind.SetAccessor: var property = member; if (isStatic(property)) { var name_2 = evaluator.nameOf(property.name); if (!schema_1.isMetadataError(name_2)) { if (property.initializer) { var value = evaluator.evaluateNode(property.initializer); recordStaticMember(name_2, value); } else { recordStaticMember(name_2, errorSym('Variable not initialized', property.name)); } } } var propertyDecorators = getDecorators(property.decorators); if (propertyDecorators) { var name_3 = evaluator.nameOf(property.name); if (!schema_1.isMetadataError(name_3)) { recordMember(name_3, { __symbolic: 'property', decorators: propertyDecorators }); } } break; } } if (members) { result.members = members; } if (statics) { result.statics = statics; } return recordEntry(result, classDeclaration); } // Collect all exported symbols from an exports clause. var exportMap = new Map(); ts.forEachChild(sourceFile, function (node) { switch (node.kind) { case ts.SyntaxKind.ExportDeclaration: var exportDeclaration = node; var moduleSpecifier = exportDeclaration.moduleSpecifier, exportClause = exportDeclaration.exportClause; if (!moduleSpecifier) { exportClause.elements.forEach(function (spec) { var exportedAs = spec.name.text; var name = (spec.propertyName || spec.name).text; exportMap.set(name, exportedAs); }); } } }); var isExportedIdentifier = function (identifier) { return exportMap.has(identifier.text); }; var isExported = function (node) { return isExport(node) || isExportedIdentifier(node.name); }; var exportedIdentifierName = function (identifier) { return exportMap.get(identifier.text) || identifier.text; }; var exportedName = function (node) { return exportedIdentifierName(node.name); }; // Predeclare classes and functions ts.forEachChild(sourceFile, function (node) { switch (node.kind) { case ts.SyntaxKind.ClassDeclaration: var classDeclaration = node; if (classDeclaration.name) { var className = classDeclaration.name.text; if (isExported(classDeclaration)) { locals.define(className, { __symbolic: 'reference', name: exportedName(classDeclaration) }); } else { locals.define(className, errorSym('Reference to non-exported class', node, { className: className })); } } break; case ts.SyntaxKind.InterfaceDeclaration: var interfaceDeclaration = node; if (interfaceDeclaration.name) { var interfaceName = interfaceDeclaration.name.text; // All references to interfaces should be converted to references to `any`. locals.define(interfaceName, { __symbolic: 'reference', name: 'any' }); } break; case ts.SyntaxKind.FunctionDeclaration: var functionDeclaration = node; if (!isExported(functionDeclaration)) { // Report references to this function as an error. var nameNode = functionDeclaration.name; if (nameNode && nameNode.text) { locals.define(nameNode.text, errorSym('Reference to a non-exported function', nameNode, { name: nameNode.text })); } } break; } }); ts.forEachChild(sourceFile, function (node) { switch (node.kind) { case ts.SyntaxKind.ExportDeclaration: // Record export declarations var exportDeclaration = node; var moduleSpecifier = exportDeclaration.moduleSpecifier, exportClause = exportDeclaration.exportClause; if (!moduleSpecifier) { // no module specifier -> export {propName as name}; if (exportClause) { exportClause.elements.forEach(function (spec) { var name = spec.name.text; // If the symbol was not already exported, export a reference since it is a // reference to an import if (!metadata || !metadata[name]) { var propNode = spec.propertyName || spec.name; var value = evaluator.evaluateNode(propNode); if (!metadata) metadata = {}; metadata[name] = recordEntry(value, node); } }); } } if (moduleSpecifier && moduleSpecifier.kind == ts.SyntaxKind.StringLiteral) { // Ignore exports that don't have string literals as exports. // This is allowed by the syntax but will be flagged as an error by the type checker. var from = moduleSpecifier.text; var moduleExport = { from: from }; if (exportClause) { moduleExport.export = exportClause.elements.map(function (spec) { return spec.propertyName ? { name: spec.propertyName.text, as: spec.name.text } : spec.name.text; }); } if (!exports) exports = []; exports.push(moduleExport); } break; case ts.SyntaxKind.ClassDeclaration: var classDeclaration = node; if (classDeclaration.name) { if (isExported(classDeclaration)) { if (!metadata) metadata = {}; metadata[exportedName(classDeclaration)] = classMetadataOf(classDeclaration); } } // Otherwise don't record metadata for the class. break; case ts.SyntaxKind.TypeAliasDeclaration: var typeDeclaration = node; if (typeDeclaration.name && isExported(typeDeclaration)) { var name_4 = exportedName(typeDeclaration); if (name_4) { if (!metadata) metadata = {}; metadata[name_4] = { __symbolic: 'interface' }; } } break; case ts.SyntaxKind.InterfaceDeclaration: var interfaceDeclaration = node; if (interfaceDeclaration.name && isExported(interfaceDeclaration)) { if (!metadata) metadata = {}; metadata[exportedName(interfaceDeclaration)] = { __symbolic: 'interface' }; } break; case ts.SyntaxKind.FunctionDeclaration: // Record functions that return a single value. Record the parameter // names substitution will be performed by the StaticReflector. var functionDeclaration = node; if (isExported(functionDeclaration) && functionDeclaration.name) { if (!metadata) metadata = {}; var name_5 = exportedName(functionDeclaration); var maybeFunc = maybeGetSimpleFunction(functionDeclaration); metadata[name_5] = maybeFunc ? recordEntry(maybeFunc.func, node) : { __symbolic: 'function' }; } break; case ts.SyntaxKind.EnumDeclaration: var enumDeclaration = node; if (isExported(enumDeclaration)) { var enumValueHolder = {}; var enumName = exportedName(enumDeclaration); var nextDefaultValue = 0; var writtenMembers = 0; for (var _i = 0, _a = enumDeclaration.members; _i < _a.length; _i++) { var member = _a[_i]; var enumValue = void 0; if (!member.initializer) { enumValue = nextDefaultValue; } else { enumValue = evaluator.evaluateNode(member.initializer); } var name_6 = undefined; if (member.name.kind == ts.SyntaxKind.Identifier) { var identifier = member.name; name_6 = identifier.text; enumValueHolder[name_6] = enumValue; writtenMembers++; } if (typeof enumValue === 'number') { nextDefaultValue = enumValue + 1; } else if (name_6) { nextDefaultValue = { __symbolic: 'binary', operator: '+', left: { __symbolic: 'select', expression: recordEntry({ __symbolic: 'reference', name: enumName }, node), name: name_6 } }; } else { nextDefaultValue = recordEntry(errorSym('Unsuppported enum member name', member.name), node); } } if (writtenMembers) { if (!metadata) metadata = {}; metadata[enumName] = recordEntry(enumValueHolder, node); } } break; case ts.SyntaxKind.VariableStatement: var variableStatement = node; var _loop_1 = function (variableDeclaration) { if (variableDeclaration.name.kind == ts.SyntaxKind.Identifier) { var nameNode = variableDeclaration.name; var varValue = void 0; if (variableDeclaration.initializer) { varValue = evaluator.evaluateNode(variableDeclaration.initializer); } else { varValue = recordEntry(errorSym('Variable not initialized', nameNode), nameNode); } var exported = false; if (isExport(variableStatement) || isExport(variableDeclaration) || isExportedIdentifier(nameNode)) { if (!metadata) metadata = {}; metadata[exportedIdentifierName(nameNode)] = recordEntry(varValue, node); exported = true; } if (typeof varValue == 'string' || typeof varValue == 'number' || typeof varValue == 'boolean') { locals.define(nameNode.text, varValue); if (exported) { locals.defineReference(nameNode.text, { __symbolic: 'reference', name: nameNode.text }); } } else if (!exported) { if (varValue && !schema_1.isMetadataError(varValue)) { locals.define(nameNode.text, recordEntry(varValue, node)); } else { locals.define(nameNode.text, recordEntry(errorSym('Reference to a local symbol', nameNode, { name: nameNode.text }), node)); } } } else { // Destructuring (or binding) declarations are not supported, // var {<identifier>[, <identifier>]+} = <expression>; // or // var [<identifier>[, <identifier}+] = <expression>; // are not supported. var report_1 = function (nameNode) { switch (nameNode.kind) { case ts.SyntaxKind.Identifier: var name_7 = nameNode; var varValue = errorSym('Destructuring not supported', name_7); locals.define(name_7.text, varValue); if (isExport(node)) { if (!metadata) metadata = {}; metadata[name_7.text] = varValue; } break; case ts.SyntaxKind.BindingElement: var bindingElement = nameNode; report_1(bindingElement.name); break; case ts.SyntaxKind.ObjectBindingPattern: case ts.SyntaxKind.ArrayBindingPattern: var bindings = nameNode; bindings.elements.forEach(report_1); break; } }; report_1(variableDeclaration.name); } }; for (var _b = 0, _c = variableStatement.declarationList.declarations; _b < _c.length; _b++) { var variableDeclaration = _c[_b]; _loop_1(variableDeclaration); } break; } }); if (metadata || exports) { if (!metadata) metadata = {}; else if (strict) { validateMetadata(sourceFile, nodeMap, metadata); } var result = { __symbolic: 'module', version: this.options.version || schema_1.VERSION, metadata: metadata }; if (exports) result.exports = exports; return result; } }; return MetadataCollector; }()); exports.MetadataCollector = MetadataCollector; // This will throw if the metadata entry given contains an error node. function validateMetadata(sourceFile, nodeMap, metadata) { var locals = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']); function validateExpression(expression) { if (!expression) { return; } else if (Array.isArray(expression)) { expression.forEach(validateExpression); } else if (typeof expression === 'object' && !expression.hasOwnProperty('__symbolic')) { Object.getOwnPropertyNames(expression).forEach(function (v) { return validateExpression(expression[v]); }); } else if (schema_1.isMetadataError(expression)) { reportError(expression); } else if (schema_1.isMetadataGlobalReferenceExpression(expression)) { if (!locals.has(expression.name)) { var reference = metadata[expression.name]; if (reference) { validateExpression(reference); } } } else if (schema_1.isFunctionMetadata(expression)) { validateFunction(expression); } else if (schema_1.isMetadataSymbolicExpression(expression)) { switch (expression.__symbolic) { case 'binary': var binaryExpression = expression; validateExpression(binaryExpression.left); validateExpression(binaryExpression.right); break; case 'call': case 'new': var callExpression = expression; validateExpression(callExpression.expression); if (callExpression.arguments) callExpression.arguments.forEach(validateExpression); break; case 'index': var indexExpression = expression; validateExpression(indexExpression.expression); validateExpression(indexExpression.index); break; case 'pre': var prefixExpression = expression; validateExpression(prefixExpression.operand); break; case 'select': var selectExpression = expression; validateExpression(selectExpression.expression); break; case 'spread': var spreadExpression = expression; validateExpression(spreadExpression.expression); break; case 'if': var ifExpression = expression; validateExpression(ifExpression.condition); validateExpression(ifExpression.elseExpression); validateExpression(ifExpression.thenExpression); break; } } } function validateMember(classData, member) { if (member.decorators) { member.decorators.forEach(validateExpression); } if (schema_1.isMethodMetadata(member) && member.parameterDecorators) { member.parameterDecorators.forEach(validateExpression); } // Only validate parameters of classes for which we know that are used with our DI if (classData.decorators && schema_1.isConstructorMetadata(member) && member.parameters) { member.parameters.forEach(validateExpression); } } function validateClass(classData) { if (classData.decorators) { classData.decorators.forEach(validateExpression); } if (classData.members) { Object.getOwnPropertyNames(classData.members) .forEach(function (name) { return classData.members[name].forEach(function (m) { return validateMember(classData, m); }); }); } if (classData.statics) { Object.getOwnPropertyNames(classData.statics).forEach(function (name) { var staticMember = classData.statics[name]; if (schema_1.isFunctionMetadata(staticMember)) { validateExpression(staticMember.value); } else { validateExpression(staticMember); } }); } } function validateFunction(functionDeclaration) { if (functionDeclaration.value) { var oldLocals = locals; if (functionDeclaration.parameters) { locals = new Set(oldLocals.values()); if (functionDeclaration.parameters) functionDeclaration.parameters.forEach(function (n) { return locals.add(n); }); } validateExpression(functionDeclaration.value); locals = oldLocals; } } function shouldReportNode(node) { if (node) { var nodeStart = node.getStart(); return !(node.pos != nodeStart && sourceFile.text.substring(node.pos, nodeStart).indexOf('@dynamic') >= 0); } return true; } function reportError(error) { var node = nodeMap.get(error); if (shouldReportNode(node)) { var lineInfo = error.line != undefined ? error.character != undefined ? ":" + (error.line + 1) + ":" + (error.character + 1) : ":" + (error.line + 1) : ''; throw new Error("" + sourceFile.fileName + lineInfo + ": Metadata collected contains an error that will be reported at runtime: " + expandedMessage(error) + ".\n " + JSON.stringify(error)); } } Object.getOwnPropertyNames(metadata).forEach(function (name) { var entry = metadata[name]; try { if (schema_1.isClassMetadata(entry)) { validateClass(entry); } } catch (e) { var node = nodeMap.get(entry); if (shouldReportNode(node)) { if (node) { var _a = sourceFile.getLineAndCharacterOfPosition(node.getStart()), line = _a.line, character = _a.character; throw new Error(sourceFile.fileName + ":" + (line + 1) + ":" + (character + 1) + ": Error encountered in metadata generated for exported symbol '" + name + "': \n " + e.message); } throw new Error("Error encountered in metadata generated for exported symbol " + name + ": \n " + e.message); } } }); } // Collect parameter names from a function. function namesOf(parameters) { var result = []; function addNamesOf(name) { if (name.kind == ts.SyntaxKind.Identifier) { var identifier = name; result.push(identifier.text); } else { var bindingPattern = name; for (var _i = 0, _a = bindingPattern.elements; _i < _a.length; _i++) { var element = _a[_i]; var name_8 = element.name; if (name_8) { addNamesOf(name_8); } } } } for (var _i = 0, parameters_2 = parameters; _i < parameters_2.length; _i++) { var parameter = parameters_2[_i]; addNamesOf(parameter.name); } return result; } function expandedMessage(error) { switch (error.message) { case 'Reference to non-exported class': if (error.context && error.context.className) { return "Reference to a non-exported class " + error.context.className + ". Consider exporting the class"; } break; case 'Variable not initialized': return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler'; case 'Destructuring not supported': return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring'; case 'Could not resolve type': if (error.context && error.context.typeName) { return "Could not resolve type " + error.context.typeName; } break; case 'Function call not supported': var prefix = error.context && error.context.name ? "Calling function '" + error.context.name + "', f" : 'F'; return prefix + 'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function'; case 'Reference to a local symbol': if (error.context && error.context.name) { return "Reference to a local (non-exported) symbol '" + error.context.name + "'. Consider exporting the symbol"; } } return error.message; } //# sourceMappingURL=collector.js.map