Project

General

Profile

1
"use strict";
2
/**
3
 * @license
4
 * Copyright Google Inc. All Rights Reserved.
5
 *
6
 * Use of this source code is governed by an MIT-style license that can be
7
 * found in the LICENSE file at https://angular.io/license
8
 */
9
var __extends = (this && this.__extends) || (function () {
10
    var extendStatics = Object.setPrototypeOf ||
11
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
12
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
13
    return function (d, b) {
14
        extendStatics(d, b);
15
        function __() { this.constructor = d; }
16
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
17
    };
18
})();
19
Object.defineProperty(exports, "__esModule", { value: true });
20
var compiler_1 = require("@angular/compiler");
21
var expression_type_1 = require("./expression_type");
22
var symbols_1 = require("./symbols");
23
function getTemplateExpressionDiagnostics(info) {
24
    var visitor = new ExpressionDiagnosticsVisitor(info, function (path, includeEvent) {
25
        return getExpressionScope(info, path, includeEvent);
26
    });
27
    compiler_1.templateVisitAll(visitor, info.templateAst);
28
    return visitor.diagnostics;
29
}
30
exports.getTemplateExpressionDiagnostics = getTemplateExpressionDiagnostics;
31
function getExpressionDiagnostics(scope, ast, query, context) {
32
    if (context === void 0) { context = {}; }
33
    var analyzer = new expression_type_1.AstType(scope, query, context);
34
    analyzer.getDiagnostics(ast);
35
    return analyzer.diagnostics;
36
}
37
exports.getExpressionDiagnostics = getExpressionDiagnostics;
38
function getReferences(info) {
39
    var result = [];
40
    function processReferences(references) {
41
        var _loop_1 = function (reference) {
42
            var type = undefined;
43
            if (reference.value) {
44
                type = info.query.getTypeSymbol(compiler_1.tokenReference(reference.value));
45
            }
46
            result.push({
47
                name: reference.name,
48
                kind: 'reference',
49
                type: type || info.query.getBuiltinType(symbols_1.BuiltinType.Any),
50
                get definition() { return getDefintionOf(info, reference); }
51
            });
52
        };
53
        for (var _i = 0, references_1 = references; _i < references_1.length; _i++) {
54
            var reference = references_1[_i];
55
            _loop_1(reference);
56
        }
57
    }
58
    var visitor = new (function (_super) {
59
        __extends(class_1, _super);
60
        function class_1() {
61
            return _super !== null && _super.apply(this, arguments) || this;
62
        }
63
        class_1.prototype.visitEmbeddedTemplate = function (ast, context) {
64
            _super.prototype.visitEmbeddedTemplate.call(this, ast, context);
65
            processReferences(ast.references);
66
        };
67
        class_1.prototype.visitElement = function (ast, context) {
68
            _super.prototype.visitElement.call(this, ast, context);
69
            processReferences(ast.references);
70
        };
71
        return class_1;
72
    }(compiler_1.RecursiveTemplateAstVisitor));
73
    compiler_1.templateVisitAll(visitor, info.templateAst);
74
    return result;
75
}
76
function getDefintionOf(info, ast) {
77
    if (info.fileName) {
78
        var templateOffset = info.offset;
79
        return [{
80
                fileName: info.fileName,
81
                span: {
82
                    start: ast.sourceSpan.start.offset + templateOffset,
83
                    end: ast.sourceSpan.end.offset + templateOffset
84
                }
85
            }];
86
    }
87
}
88
function getVarDeclarations(info, path) {
89
    var result = [];
90
    var current = path.tail;
91
    while (current) {
92
        if (current instanceof compiler_1.EmbeddedTemplateAst) {
93
            var _loop_2 = function (variable) {
94
                var name_1 = variable.name;
95
                // Find the first directive with a context.
96
                var context = current.directives.map(function (d) { return info.query.getTemplateContext(d.directive.type.reference); })
97
                    .find(function (c) { return !!c; });
98
                // Determine the type of the context field referenced by variable.value.
99
                var type = undefined;
100
                if (context) {
101
                    var value = context.get(variable.value);
102
                    if (value) {
103
                        type = value.type;
104
                        var kind = info.query.getTypeKind(type);
105
                        if (kind === symbols_1.BuiltinType.Any || kind == symbols_1.BuiltinType.Unbound) {
106
                            // The any type is not very useful here. For special cases, such as ngFor, we can do
107
                            // better.
108
                            type = refinedVariableType(type, info, current);
109
                        }
110
                    }
111
                }
112
                if (!type) {
113
                    type = info.query.getBuiltinType(symbols_1.BuiltinType.Any);
114
                }
115
                result.push({
116
                    name: name_1,
117
                    kind: 'variable', type: type, get definition() { return getDefintionOf(info, variable); }
118
                });
119
            };
120
            for (var _i = 0, _a = current.variables; _i < _a.length; _i++) {
121
                var variable = _a[_i];
122
                _loop_2(variable);
123
            }
124
        }
125
        current = path.parentOf(current);
126
    }
127
    return result;
128
}
129
function refinedVariableType(type, info, templateElement) {
130
    // Special case the ngFor directive
131
    var ngForDirective = templateElement.directives.find(function (d) {
132
        var name = compiler_1.identifierName(d.directive.type);
133
        return name == 'NgFor' || name == 'NgForOf';
134
    });
135
    if (ngForDirective) {
136
        var ngForOfBinding = ngForDirective.inputs.find(function (i) { return i.directiveName == 'ngForOf'; });
137
        if (ngForOfBinding) {
138
            var bindingType = new expression_type_1.AstType(info.members, info.query, {}).getType(ngForOfBinding.value);
139
            if (bindingType) {
140
                var result = info.query.getElementType(bindingType);
141
                if (result) {
142
                    return result;
143
                }
144
            }
145
        }
146
    }
147
    // We can't do better, return any
148
    return info.query.getBuiltinType(symbols_1.BuiltinType.Any);
149
}
150
function getEventDeclaration(info, includeEvent) {
151
    var result = [];
152
    if (includeEvent) {
153
        // TODO: Determine the type of the event parameter based on the Observable<T> or EventEmitter<T>
154
        // of the event.
155
        result = [{ name: '$event', kind: 'variable', type: info.query.getBuiltinType(symbols_1.BuiltinType.Any) }];
156
    }
157
    return result;
158
}
159
function getExpressionScope(info, path, includeEvent) {
160
    var result = info.members;
161
    var references = getReferences(info);
162
    var variables = getVarDeclarations(info, path);
163
    var events = getEventDeclaration(info, includeEvent);
164
    if (references.length || variables.length || events.length) {
165
        var referenceTable = info.query.createSymbolTable(references);
166
        var variableTable = info.query.createSymbolTable(variables);
167
        var eventsTable = info.query.createSymbolTable(events);
168
        result = info.query.mergeSymbolTable([result, referenceTable, variableTable, eventsTable]);
169
    }
170
    return result;
171
}
172
exports.getExpressionScope = getExpressionScope;
173
var ExpressionDiagnosticsVisitor = (function (_super) {
174
    __extends(ExpressionDiagnosticsVisitor, _super);
175
    function ExpressionDiagnosticsVisitor(info, getExpressionScope) {
176
        var _this = _super.call(this) || this;
177
        _this.info = info;
178
        _this.getExpressionScope = getExpressionScope;
179
        _this.diagnostics = [];
180
        _this.path = new compiler_1.AstPath([]);
181
        return _this;
182
    }
183
    ExpressionDiagnosticsVisitor.prototype.visitDirective = function (ast, context) {
184
        // Override the default child visitor to ignore the host properties of a directive.
185
        if (ast.inputs && ast.inputs.length) {
186
            compiler_1.templateVisitAll(this, ast.inputs, context);
187
        }
188
    };
189
    ExpressionDiagnosticsVisitor.prototype.visitBoundText = function (ast) {
190
        this.push(ast);
191
        this.diagnoseExpression(ast.value, ast.sourceSpan.start.offset, false);
192
        this.pop();
193
    };
194
    ExpressionDiagnosticsVisitor.prototype.visitDirectiveProperty = function (ast) {
195
        this.push(ast);
196
        this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
197
        this.pop();
198
    };
199
    ExpressionDiagnosticsVisitor.prototype.visitElementProperty = function (ast) {
200
        this.push(ast);
201
        this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
202
        this.pop();
203
    };
204
    ExpressionDiagnosticsVisitor.prototype.visitEvent = function (ast) {
205
        this.push(ast);
206
        this.diagnoseExpression(ast.handler, this.attributeValueLocation(ast), true);
207
        this.pop();
208
    };
209
    ExpressionDiagnosticsVisitor.prototype.visitVariable = function (ast) {
210
        var directive = this.directiveSummary;
211
        if (directive && ast.value) {
212
            var context = this.info.query.getTemplateContext(directive.type.reference);
213
            if (context && !context.has(ast.value)) {
214
                if (ast.value === '$implicit') {
215
                    this.reportError('The template context does not have an implicit value', spanOf(ast.sourceSpan));
216
                }
217
                else {
218
                    this.reportError("The template context does not defined a member called '" + ast.value + "'", spanOf(ast.sourceSpan));
219
                }
220
            }
221
        }
222
    };
223
    ExpressionDiagnosticsVisitor.prototype.visitElement = function (ast, context) {
224
        this.push(ast);
225
        _super.prototype.visitElement.call(this, ast, context);
226
        this.pop();
227
    };
228
    ExpressionDiagnosticsVisitor.prototype.visitEmbeddedTemplate = function (ast, context) {
229
        var previousDirectiveSummary = this.directiveSummary;
230
        this.push(ast);
231
        // Find directive that refernces this template
232
        this.directiveSummary =
233
            ast.directives.map(function (d) { return d.directive; }).find(function (d) { return hasTemplateReference(d.type); });
234
        // Process children
235
        _super.prototype.visitEmbeddedTemplate.call(this, ast, context);
236
        this.pop();
237
        this.directiveSummary = previousDirectiveSummary;
238
    };
239
    ExpressionDiagnosticsVisitor.prototype.attributeValueLocation = function (ast) {
240
        var path = compiler_1.findNode(this.info.htmlAst, ast.sourceSpan.start.offset);
241
        var last = path.tail;
242
        if (last instanceof compiler_1.Attribute && last.valueSpan) {
243
            // Add 1 for the quote.
244
            return last.valueSpan.start.offset + 1;
245
        }
246
        return ast.sourceSpan.start.offset;
247
    };
248
    ExpressionDiagnosticsVisitor.prototype.diagnoseExpression = function (ast, offset, includeEvent) {
249
        var _this = this;
250
        var scope = this.getExpressionScope(this.path, includeEvent);
251
        (_a = this.diagnostics).push.apply(_a, getExpressionDiagnostics(scope, ast, this.info.query, {
252
            event: includeEvent
253
        }).map(function (d) { return ({
254
            span: offsetSpan(d.ast.span, offset + _this.info.offset),
255
            kind: d.kind,
256
            message: d.message
257
        }); }));
258
        var _a;
259
    };
260
    ExpressionDiagnosticsVisitor.prototype.push = function (ast) { this.path.push(ast); };
261
    ExpressionDiagnosticsVisitor.prototype.pop = function () { this.path.pop(); };
262
    ExpressionDiagnosticsVisitor.prototype.reportError = function (message, span) {
263
        if (span) {
264
            this.diagnostics.push({ span: offsetSpan(span, this.info.offset), kind: expression_type_1.DiagnosticKind.Error, message: message });
265
        }
266
    };
267
    ExpressionDiagnosticsVisitor.prototype.reportWarning = function (message, span) {
268
        this.diagnostics.push({ span: offsetSpan(span, this.info.offset), kind: expression_type_1.DiagnosticKind.Warning, message: message });
269
    };
270
    return ExpressionDiagnosticsVisitor;
271
}(compiler_1.RecursiveTemplateAstVisitor));
272
function hasTemplateReference(type) {
273
    if (type.diDeps) {
274
        for (var _i = 0, _a = type.diDeps; _i < _a.length; _i++) {
275
            var diDep = _a[_i];
276
            if (diDep.token && diDep.token.identifier &&
277
                compiler_1.identifierName(diDep.token.identifier) == 'TemplateRef')
278
                return true;
279
        }
280
    }
281
    return false;
282
}
283
function offsetSpan(span, amount) {
284
    return { start: span.start + amount, end: span.end + amount };
285
}
286
function spanOf(sourceSpan) {
287
    return { start: sourceSpan.start.offset, end: sourceSpan.end.offset };
288
}
289
//# sourceMappingURL=expression_diagnostics.js.map
(5-5/15)