Web Inspector: Add infrastructure for eslint based static analyzer
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Controllers / AnalyzerManager.js
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 WebInspector.AnalyzerManager = function()
27 {
28     WebInspector.Object.call(this);
29
30     this._eslintConfig = {
31         env: {
32             "browser": true,
33             "node": false
34         },
35         globals: {
36             "document": true
37         },
38         rules: {
39             "consistent-return": 2,
40             "curly": 0,
41             "eqeqeq": 0,
42             "new-parens": 0,
43             "no-comma-dangle": 0,
44             "no-console": 0,
45             "no-constant-condition": 0,
46             "no-extra-bind": 2,
47             "no-extra-semi": 2,
48             "no-proto": 0,
49             "no-return-assign": 2,
50             "no-trailing-spaces": 2,
51             "no-underscore-dangle": 0,
52             "no-unused-expressions": 2,
53             "no-wrap-func": 2,
54             "semi": 2,
55             "space-infix-ops": 2,
56             "space-return-throw-case": 2,
57             "strict": 0,
58             "valid-typeof": 2
59         }
60     };
61
62     this._sourceCodeMessagesMap = new WeakMap;
63
64     WebInspector.SourceCode.addEventListener(WebInspector.SourceCode.Event.ContentDidChange, this._handleSourceCodeContentDidChange, this);
65 };
66
67 WebInspector.AnalyzerManager._typeAnalyzerMap = new Map;
68 WebInspector.AnalyzerManager._typeAnalyzerMap.set(WebInspector.Resource.Type.Script, eslint);
69
70 WebInspector.AnalyzerManager.prototype = {
71     constructor: WebInspector.AnalyzerManager,
72     __proto__: WebInspector.Object.prototype,
73
74     // Public
75
76     getAnalyzerMessagesForSourceCode: function(sourceCode)
77     {
78         return new Promise(function(resolve, reject) {
79             var analyzer = WebInspector.AnalyzerManager._typeAnalyzerMap.get(sourceCode.type);
80             if (!analyzer) {
81                 reject(new Error("This resource type cannot be analyzed."));
82                 return;
83             }
84
85             if (this._sourceCodeMessagesMap.has(sourceCode)) {
86                 resolve(this._sourceCodeMessagesMap.get(sourceCode));
87                 return;
88             }
89
90             function retrieveAnalyzerMessages()
91             {
92                 var analyzerMessages = [];
93                 var rawAnalyzerMessages = analyzer.verify(sourceCode.content, this._eslintConfig);
94
95                 // Raw line and column numbers are one-based. SourceCodeLocation expects them to be zero-based so we subtract 1 from each.
96                 for (var rawAnalyzerMessage of rawAnalyzerMessages)
97                     analyzerMessages.push(new WebInspector.AnalyzerMessage(new WebInspector.SourceCodeLocation(sourceCode, rawAnalyzerMessage.line - 1, rawAnalyzerMessage.column - 1), rawAnalyzerMessage.message, rawAnalyzerMessage.ruleId));
98
99                 this._sourceCodeMessagesMap.set(sourceCode, analyzerMessages);
100
101                 resolve(analyzerMessages);
102             }
103
104             sourceCode.requestContent(retrieveAnalyzerMessages.bind(this));
105         }.bind(this));
106     },
107
108     sourceCodeCanBeAnalyzed: function(sourceCode)
109     {
110         return sourceCode.type === WebInspector.Resource.Type.Script;
111     },
112
113     // Private
114
115     _handleSourceCodeContentDidChange: function(event)
116     {
117         var sourceCode = event.target;
118
119         // Since sourceCode has changed, remove it and its messages from the map so getAnalyzerMessagesForSourceCode will have to reanalyze the next time it is called.
120         this._sourceCodeMessagesMap.delete(sourceCode);
121     }
122 };