1 | var traverse = require('ast-traverse');
|
2 | var astUtils = require('sl-esprima-ast-utils');
|
3 | var globalErrorHandler = require('../global-error-handler');
|
4 | var babelParser = require("@babel/parser");
|
5 | var createNodeKey = require('./create-node-key').createNodeKey;
|
6 | var AstNodeTypes = require("./ast-node-types");
|
7 | var consoleFileLogger = require("../sl-logger.js").consoleFileLogger;
|
8 | var MethodSignature = require("./method-signature");
|
9 | var BranchSignature = require("./branch-signature");
|
10 | var globalMethodIndexer = require("./globalMethodIndexer");
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | var FileSignature = function (absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer) {
|
23 | this.absolutePath = absolutePtah;
|
24 | this.relativePath = relativePath;
|
25 | this.methods = [];
|
26 | this.branches = [];
|
27 | this.content = content;
|
28 | this.sourceMaps = sourceMaps;
|
29 | this.opts = opts;
|
30 | this.scmFilesContainer = scmFilesContainer;
|
31 | this.hasSourceMaps = !!sourceMaps;
|
32 | this.logger = consoleFileLogger("FileSignature");
|
33 | this.positionToMethodMap = {};
|
34 | this.positionToBranchMap = {};
|
35 | this.methodIndex = 0;
|
36 | this.branchIndex = 0;
|
37 | };
|
38 |
|
39 | FileSignature.prototype._createSignature = function () {
|
40 | this.prepareContent();
|
41 | var ast = this.parseContentToAST();
|
42 | this.traverseAST(ast);
|
43 | };
|
44 |
|
45 | FileSignature.prototype.createDTO = function(){
|
46 | return {
|
47 | methods: this.methods,
|
48 | branches: this.branches,
|
49 | filename: this.relativePath,
|
50 | hasSourceMaps: this.hasSourceMaps
|
51 | }
|
52 | };
|
53 |
|
54 | FileSignature.prototype.parseContentToAST = function () {
|
55 | var sourceType = (this.opts && this.opts.es6Modules) ? 'module' : 'script';
|
56 |
|
57 | var plugins = ["*", ["decorators", {decoratorsBeforeExport: true}]];
|
58 | if (this.opts && this.opts.babylonPlugins) {
|
59 | plugins = plugins.concat(this.opts.babylonPlugins);
|
60 | }
|
61 | return babelParser.parse(this.content, {
|
62 |
|
63 | sourceType: sourceType,
|
64 | plugins: plugins
|
65 | })
|
66 | };
|
67 | FileSignature.prototype.traverseAST = function (ast) {
|
68 | var context = this;
|
69 | var nodeToParent = {};
|
70 | var activeMethodKey = null;
|
71 | var activeMethodIndex = null;
|
72 | traverse(ast, {
|
73 | pre: function (node, parentNode) {
|
74 | var key = createNodeKey(node);
|
75 | nodeToParent[key] = parentNode;
|
76 | if (AstNodeTypes.isMethodNode(node)) {
|
77 |
|
78 | if(activeMethodKey == null) {
|
79 | activeMethodKey = createNodeKey(node);
|
80 | }
|
81 | globalMethodIndexer.incrementIndex();
|
82 | |
83 |
|
84 |
|
85 |
|
86 | activeMethodIndex = globalMethodIndexer.getCurrentIndex();
|
87 | context.addMethod(node, parentNode, nodeToParent);
|
88 | } else if (context.opts.isBranchCoverage && AstNodeTypes.isBranchNode(node)) {
|
89 | context.addBranch(node, parentNode, activeMethodKey == null, activeMethodIndex)
|
90 | }
|
91 | },
|
92 | post: function (node) {
|
93 | if(AstNodeTypes.isMethodNode(node)){
|
94 |
|
95 | if(activeMethodKey === createNodeKey(node)) {
|
96 | activeMethodKey = null;
|
97 | }
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 | activeMethodIndex --;
|
104 | }
|
105 | }
|
106 | });
|
107 | };
|
108 | FileSignature.prototype.addMethod = function (node, parentNode, nodeToParent) {
|
109 | var method = MethodSignature.create(node, parentNode, nodeToParent, this.absolutePath,
|
110 | this.relativePath, this.logger);
|
111 | method.srcData = method.generatedData;
|
112 | this.methods.push(method.createDTO());
|
113 | this.positionToMethodMap[method.generatedData.position] = this.methodIndex;
|
114 | this.methodIndex++;
|
115 | };
|
116 | FileSignature.prototype.addBranch = function (node, parentNode, isGlobalBranch, activeMethodIndex) {
|
117 | var context = this;
|
118 | var branches = [];
|
119 | if (node.type !== AstNodeTypes.LogicalExpression) {
|
120 | branches = branches.concat(this.getBranchesToAdd(node, isGlobalBranch, activeMethodIndex));
|
121 | }
|
122 |
|
123 |
|
124 | if (node.type == AstNodeTypes.LogicalExpression && (parentNode == undefined || parentNode.type !== AstNodeTypes.LogicalExpression)) {
|
125 | branches = branches.concat(this.getBranchesToAdd(node, isGlobalBranch, activeMethodIndex));
|
126 | }
|
127 |
|
128 | branches.forEach(function (branch) {
|
129 | branch.srcData = branch.generatedData;
|
130 | context.branches.push(branch.createDTO());
|
131 | context.positionToBranchMap[branch.generatedData.position] = context.branchIndex;
|
132 | context.branchIndex ++;
|
133 | })
|
134 | };
|
135 | FileSignature.prototype.prepareContent = function () {
|
136 | this.content = trimShebang(this.content);
|
137 | };
|
138 | FileSignature.prototype.getBranchesToAdd = function (node, isGlobalBranch, activeMethodIndex) {
|
139 | var branchesToAdd = [];
|
140 | var context = this;
|
141 | var nodeKey = createNodeKey(node)
|
142 | switch (node.type) {
|
143 |
|
144 | case AstNodeTypes.IfStatement:
|
145 | case AstNodeTypes.ConditionalExpression:
|
146 | var branchIndex = 0;
|
147 |
|
148 |
|
149 | if (node.consequent !== null) {
|
150 | var branch = BranchSignature.create(node.consequent, node, node.type, branchIndex++, this.absolutePath, this.relativePath,
|
151 | isGlobalBranch, activeMethodIndex, this.logger);
|
152 | branchesToAdd.push(branch);
|
153 | }
|
154 |
|
155 |
|
156 | var clonedNode = null;
|
157 | if (node.alternate == null) {
|
158 | clonedNode = createEmptyAlternate(node);
|
159 | }
|
160 | var nodeToAdd = clonedNode || node;
|
161 | var branch = BranchSignature.create(nodeToAdd.alternate, nodeToAdd, node.type, branchIndex++, this.absolutePath, this.relativePath,
|
162 | isGlobalBranch, activeMethodIndex, this.logger);
|
163 | branchesToAdd.push(branch);
|
164 | break;
|
165 | case AstNodeTypes.SwitchStatement:
|
166 | var branchIndex = 0;
|
167 |
|
168 |
|
169 | node.cases.forEach(function (caseNode) {
|
170 | if (caseNode !== null) {
|
171 | var branch = BranchSignature.create(caseNode, node, node.type, branchIndex++, context.absolutePath, context.relativePath,
|
172 | isGlobalBranch, activeMethodIndex, context.logger);
|
173 | branchesToAdd.push(branch);
|
174 | }
|
175 | });
|
176 | break;
|
177 | case AstNodeTypes.LogicalExpression:
|
178 | var branchIndex = 0;
|
179 | var leaves = [];
|
180 | findLogicalExpressionLeaves(node, leaves);
|
181 | leaves.forEach(function (leafNode) {
|
182 | if (leafNode !== null) {
|
183 | var branch = BranchSignature.create(leafNode, node, node.type, branchIndex++, context.absolutePath, context.relativePath,
|
184 | isGlobalBranch, activeMethodIndex, context.logger);
|
185 | branchesToAdd.push(branch);
|
186 | }
|
187 | });
|
188 | break;
|
189 | }
|
190 | return branchesToAdd;
|
191 | };
|
192 |
|
193 |
|
194 | FileSignature.create = function (absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer) {
|
195 | var signature = new this(absolutePtah, relativePath, content, sourceMaps, opts, scmFilesContainer);
|
196 | signature._createSignature();
|
197 | return signature;
|
198 | }
|
199 |
|
200 |
|
201 | function findLogicalExpressionLeaves(node, leaves) {
|
202 | if (node.type === AstNodeTypes.LogicalExpression) {
|
203 | findLogicalExpressionLeaves(node.left, leaves);
|
204 | findLogicalExpressionLeaves(node.right, leaves);
|
205 | } else {
|
206 | leaves.push(node);
|
207 | }
|
208 | }
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | function trimShebang(text) {
|
214 | return text.replace(/^#!([^\r\n]+)/, function (match, captured) {
|
215 | return "//" + captured;
|
216 | });
|
217 | }
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 |
|
225 | function _createEmptyBlock() {
|
226 | return {
|
227 | type: AstNodeTypes.BlockStatement,
|
228 | body: []
|
229 | }
|
230 | }
|
231 |
|
232 | function createEmptyAlternate(node) {
|
233 |
|
234 | var clonedNode = astUtils.clone(node);
|
235 | clonedNode.alternate = _createEmptyBlock();
|
236 |
|
237 |
|
238 | clonedNode.alternate.loc = node.consequent !== null ? node.consequent.loc : node.loc;
|
239 | return clonedNode;
|
240 | }
|
241 |
|
242 | module.exports = FileSignature;
|