GitHub PullRequestReviewCommentEvent at 2014-10-16 09:34:19
Repository: gemini
Actor: unlok
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/bem/gemini/pulls/comments/18945838","id":18945838,"diff_hunk":"@@ -44,105 +48,311 @@ module.exports = inherit({\n                 return qfs.read(path.join(__dirname, 'coverage.hbs'));\n             })\n             .then(function(tmpl) {\n-                return q.all(Object.keys(byfile).map(function(file) {\n-                    return http.read({url: file})\n-                        .then(function(fileContent) {\n-                            fileContent += '';\n-                            var line = 0,\n-                                lines = fileContent.replace(/\\\\r\\\\n/g, '\\n').split('\\n'),\n-                                ast = css.parse(fileContent),\n-                                ctx = {\n-                                    media: 0\n-                                },\n-                                out = [];\n-\n-                            for (var r = 0; r < ast.stylesheet.rules.length; r++) {\n-                                line = processRule(\n-                                    ast.stylesheet.rules[r],\n-                                    {\n-                                        line: line,\n-                                        lines: lines,\n-                                        byfile: byfile,\n-                                        file: file,\n-                                        out: out\n-                                    },\n-                                    ctx);\n-                            }\n-\n-                            return qfs.write(\n-                                path.join('gemini-coverage', url.parse(file).pathname.replace(/\\//g, '_') + '.html'),\n-                                hb.compile(tmpl)({blocks: out}));\n+                var reports = [];\n+\n+                return q.all(Object.keys(_this.byfile).map(function(file) {\n+                    return _this.processFile(file, tmpl, reports);\n+                }))\n+                .then(function() {\n+                    reports.sort(function(a, b) {\n+                        return a.source - b.source;\n+                    });\n+                    return writeIndex(reports, _this.covDir);\n+                })\n+                .then(function() {\n+                    return copyResources(_this.covDir);\n+                });\n+            });\n+    },\n+\n+    processFile: function(file, tmpl, reports) {\n+        var _this = this,\n+            cssPath = path.resolve(this.config.sourceRoot, url.parse(file).pathname.substring(1));\n+\n+        return http.read({url: file})\n+            .then(function(content) {\n+                var ast = css.parse(content += '');\n+                return q.all([\n+                    ast,\n+                    content,\n+                    _this.getSourceMap(ast, cssPath, file)\n+                ]);\n+            })\n+            .spread(function(ast, fileContent, map) {\n+                fileContent += '';\n+                var lines = fileContent.split(/\\r?\\n/g),\n+                    ctx = {media: 0},\n+                    out = {};\n+\n+                for (var r = 0; r < ast.stylesheet.rules.length; r++) {\n+                    _this.processRule(\n+                        ast.stylesheet.rules[r],\n+                        {\n+                            lines: lines,\n+                            file: file,\n+                            out: out,\n+                            map: map,\n+                            docDir: path.dirname(cssPath)\n+                        },\n+                        ctx);\n+                }\n+\n+                return q.all(Object.keys(out).map(function(src) {\n+                    var reportFile = src.replace(/\\//g, '_') + '.html',\n+                        reportPath = path.join(_this.covDir, reportFile);\n+\n+                    return _this.makeReport(\n+                        {\n+                            file: path.resolve(_this.config.sourceRoot, src),\n+                            url: url.resolve(_this.config.rootUrl, src)\n+                        },\n+                        reportPath,\n+                        {\n+                            data: out[src],\n+                            tmpl: tmpl\n+                        })\n+                        .then(function(rulesStat) {\n+                            reports.push({\n+                                source: src,\n+                                report: reportFile,\n+                                stat: rulesStat\n+                            });\n                         });\n                 }));\n             });\n-    }\n-});\n+    },\n \n-function processRule(rule, opts, ctx) {\n-    var line = opts.line;\n-    if (rule.type !== 'rule') {\n-        if (rule.type === 'media') {\n-            rule.rules.forEach(function(childRule) {\n-                line = processRule(\n-                    childRule,\n-                    {\n-                        line: line,\n-                        lines: opts.lines,\n-                        byfile: opts.byfile,\n-                        file: opts.file,\n-                        out: opts.out,\n-                        group: true\n-                    },\n-                    ctx);\n-            });\n+    getSourceMap: function(ast, cssPath, cssUrl) {\n+        var sourceMapUrl = this.findSourceMapPragma(ast);\n \n-            ctx.media++;\n+        if (sourceMapUrl) {\n+            return readUrlOrFile(\n+                    url.resolve(cssUrl, sourceMapUrl),\n+                    path.resolve(path.dirname(cssPath), sourceMapUrl)\n+                )\n+                .fail(function(err) {\n+                    if (err.code !== 'ENOENT') {\n+                        return q.reject(err);\n+                    }\n+                })\n+                .then(function(map) {\n+                    return new SourceMapConsumer(JSON.parse(map));\n+                });\n         }\n+    },\n+\n+    findSourceMapPragma: function(ast) {\n+        var re = /^#\\s*sourceMappingUrl=([^\\s]+)/i;\n+\n+        for (var r = 0; r < ast.stylesheet.rules.length; r++) {\n+            var rule = ast.stylesheet.rules[r];\n+            if (rule.type !== 'comment') {\n+                continue;\n+            }\n \n-        if (line < rule.position.end.line) {\n-            line = pushLines(line, rule.position.end.line, opts);\n+            var match = rule.comment.match(re);\n+            if (match) {\n+                return decodeURI(match[1]);\n+            }\n         }\n+    },\n \n-        return line;\n-    }\n+    processRule: function(rule, opts, ctx) {\n+        if (rule.type !== 'rule') {\n+            if (rule.type === 'media') {\n+                rule.rules.forEach(function(childRule) {\n+                    this.processRule(\n+                        childRule,\n+                        {\n+                            lines: opts.lines,\n+                            file: opts.file,\n+                            out: opts.out,\n+                            group: true,\n+                            map: opts.map,\n+                            docDir: opts.docDir\n+                        },\n+                        ctx);\n+                }, this);\n \n-    line = pushLines(line, rule.position.start.line - 1, opts);\n+                ctx.media++;\n+            }\n \n-    var cls = 'none';\n-    for (var sel = 0; sel < rule.selectors.length; sel++) {\n-        var key = rule.selectors[sel];\n-        if (opts.group) {\n-            key = '?' + ctx.media + ':' + key;\n+            return;\n         }\n \n-        var selector = opts.byfile[opts.file][key];\n-        if (selector) {\n-            cls = 'partial';\n-            if (selector.within) {\n-                cls = 'covered';\n+        var cls = 'none';\n+        for (var sel = 0; sel < rule.selectors.length; sel++) {\n+            var key = rule.selectors[sel];\n+            if (opts.group) {\n+                key = '?' + ctx.media + ':' + key;\n             }\n-            break;\n+\n+            var selector = this.byfile[opts.file][key];\n+            if (selector) {\n+                cls = 'partial';\n+                if (selector.within) {\n+                    cls = 'covered';\n+                }\n+                break;\n+            }\n+        }\n+\n+        var sourceStart = getPosition(rule.position.start, opts.file, opts.map),\n+            sourceEnd = getPosition(rule.position.end, opts.file, opts.map),\n+            src = sourceStart.source?\n+                path.relative(this.config.sourceRoot, path.resolve(opts.docDir, sourceStart.source)) :\n+                opts.file.replace(this.config.rootUrl, '').replace(/^\\//, '');\n+\n+        (opts.out[src] = opts.out[src] || []).push({\n+            start: {\n+                line: sourceStart.line,\n+                column: sourceStart.column\n+            },\n+            end: {\n+                line: sourceEnd.line,\n+                column: sourceEnd.column\n+            },\n+            type: cls\n+        });\n+    },\n+\n+    makeReport: function(source, dest, opts) {\n+        var _this = this,\n+            data = opts.data,\n+            url = source.url,\n+            file = source.file,\n+            coveredCount = 0,\n+            stat;\n+\n+        return readUrlOrFile(url, file)\n+            .then(function(content) {\n+                var lines = content.toString().replace(/\\\\r\\\\n/g, '\\n').split('\\n');\n+                // cover into <pre> blocks css lines having some coverage state\n+                for (var b = data.length - 1; b >= 0; b--) {\n+                    var block = data[b];\n+\n+                    for (var l = block.start.line - 1; l < block.end.line; l++) {\n+                        lines[l] = '<pre class=\"' + block.type + '\">' + htmlEscape(lines[l]) + ' </pre>';","path":"lib/coverage.js","position":292,"original_position":292,"commit_id":"7c0a4f69631aa349c6b79a46cb69767cdba3038b","original_commit_id":"7c0a4f69631aa349c6b79a46cb69767cdba3038b","user":{"login":"unlok","id":3048454,"avatar_url":"https://avatars.githubusercontent.com/u/3048454?v=2","gravatar_id":"","url":"https://api.github.com/users/unlok","html_url":"https://github.com/unlok","followers_url":"https://api.github.com/users/unlok/followers","following_url":"https://api.github.com/users/unlok/following{/other_user}","gists_url":"https://api.github.com/users/unlok/gists{/gist_id}","starred_url":"https://api.github.com/users/unlok/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unlok/subscriptions","organizations_url":"https://api.github.com/users/unlok/orgs","repos_url":"https://api.github.com/users/unlok/repos","events_url":"https://api.github.com/users/unlok/events{/privacy}","received_events_url":"https://api.github.com/users/unlok/received_events","type":"User","site_admin":false},"body":"Почему часть про шаблон находится в логике?\r\nНужно отдавать объекты в шаблон и там итерироваться.","created_at":"2014-10-16T09:34:19Z","updated_at":"2014-10-16T09:34:19Z","html_url":"https://github.com/bem/gemini/pull/71#discussion_r18945838","pull_request_url":"https://api.github.com/repos/bem/gemini/pulls/71","_links":{"self":{"href":"https://api.github.com/repos/bem/gemini/pulls/comments/18945838"},"html":{"href":"https://github.com/bem/gemini/pull/71#discussion_r18945838"},"pull_request":{"href":"https://api.github.com/repos/bem/gemini/pulls/71"}}},"pull_request":{"url":"https://api.github.com/repos/bem/gemini/pulls/71","id":22639526,"html_url":"https://github.com/bem/gemini/pull/71","diff_url":"https://github.com/bem/gemini/pull/71.diff","patch_url":"https://github.com/bem/gemini/pull/71.patch","issue_url":"https://api.github.com/repos/bem/gemini/issues/71","number":71,"state":"closed","locked":false,"title":"Support for source maps in coverage reports","user":{"login":"scf2k","id":1437003,"avatar_url":"https://avatars.githubusercontent.com/u/1437003?v=2","gravatar_id":"","url":"https://api.github.com/users/scf2k","html_url":"https://github.com/scf2k","followers_url":"https://api.github.com/users/scf2k/followers","following_url":"https://api.github.com/users/scf2k/following{/other_user}","gists_url":"https://api.github.com/users/scf2k/gists{/gist_id}","starred_url":"https://api.github.com/users/scf2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scf2k/subscriptions","organizations_url":"https://api.github.com/users/scf2k/orgs","repos_url":"https://api.github.com/users/scf2k/repos","events_url":"https://api.github.com/users/scf2k/events{/privacy}","received_events_url":"https://api.github.com/users/scf2k/received_events","type":"User","site_admin":false},"body":"/cc @SevInf @arikon ","created_at":"2014-10-13T11:42:05Z","updated_at":"2014-10-16T09:34:19Z","closed_at":"2014-10-16T09:24:57Z","merged_at":"2014-10-16T09:24:57Z","merge_commit_sha":"5fb791abf2484975891312002c829cb38a4fdc25","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/bem/gemini/pulls/71/commits","review_comments_url":"https://api.github.com/repos/bem/gemini/pulls/71/comments","review_comment_url":"https://api.github.com/repos/bem/gemini/pulls/comments/{number}","comments_url":"https://api.github.com/repos/bem/gemini/issues/71/comments","statuses_url":"https://api.github.com/repos/bem/gemini/statuses/7c0a4f69631aa349c6b79a46cb69767cdba3038b","head":{"label":"bem:feature/sourcemap","ref":"feature/sourcemap","sha":"7c0a4f69631aa349c6b79a46cb69767cdba3038b","user":{"login":"bem","id":223412,"avatar_url":"https://avatars.githubusercontent.com/u/223412?v=2","gravatar_id":"","url":"https://api.github.com/users/bem","html_url":"https://github.com/bem","followers_url":"https://api.github.com/users/bem/followers","following_url":"https://api.github.com/users/bem/following{/other_user}","gists_url":"https://api.github.com/users/bem/gists{/gist_id}","starred_url":"https://api.github.com/users/bem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bem/subscriptions","organizations_url":"https://api.github.com/users/bem/orgs","repos_url":"https://api.github.com/users/bem/repos","events_url":"https://api.github.com/users/bem/events{/privacy}","received_events_url":"https://api.github.com/users/bem/received_events","type":"Organization","site_admin":false},"repo":{"id":16734168,"name":"gemini","full_name":"bem/gemini","owner":{"login":"bem","id":223412,"avatar_url":"https://avatars.githubusercontent.com/u/223412?v=2","gravatar_id":"","url":"https://api.github.com/users/bem","html_url":"https://github.com/bem","followers_url":"https://api.github.com/users/bem/followers","following_url":"https://api.github.com/users/bem/following{/other_user}","gists_url":"https://api.github.com/users/bem/gists{/gist_id}","starred_url":"https://api.github.com/users/bem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bem/subscriptions","organizations_url":"https://api.github.com/users/bem/orgs","repos_url":"https://api.github.com/users/bem/repos","events_url":"https://api.github.com/users/bem/events{/privacy}","received_events_url":"https://api.github.com/users/bem/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bem/gemini","description":"Utility for regression testing of web pages using screenshots","fork":false,"url":"https://api.github.com/repos/bem/gemini","forks_url":"https://api.github.com/repos/bem/gemini/forks","keys_url":"https://api.github.com/repos/bem/gemini/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bem/gemini/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bem/gemini/teams","hooks_url":"https://api.github.com/repos/bem/gemini/hooks","issue_events_url":"https://api.github.com/repos/bem/gemini/issues/events{/number}","events_url":"https://api.github.com/repos/bem/gemini/events","assignees_url":"https://api.github.com/repos/bem/gemini/assignees{/user}","branches_url":"https://api.github.com/repos/bem/gemini/branches{/branch}","tags_url":"https://api.github.com/repos/bem/gemini/tags","blobs_url":"https://api.github.com/repos/bem/gemini/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bem/gemini/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bem/gemini/git/refs{/sha}","trees_url":"https://api.github.com/repos/bem/gemini/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bem/gemini/statuses/{sha}","languages_url":"https://api.github.com/repos/bem/gemini/languages","stargazers_url":"https://api.github.com/repos/bem/gemini/stargazers","contributors_url":"https://api.github.com/repos/bem/gemini/contributors","subscribers_url":"https://api.github.com/repos/bem/gemini/subscribers","subscription_url":"https://api.github.com/repos/bem/gemini/subscription","commits_url":"https://api.github.com/repos/bem/gemini/commits{/sha}","git_commits_url":"https://api.github.com/repos/bem/gemini/git/commits{/sha}","comments_url":"https://api.github.com/repos/bem/gemini/comments{/number}","issue_comment_url":"https://api.github.com/repos/bem/gemini/issues/comments/{number}","contents_url":"https://api.github.com/repos/bem/gemini/contents/{+path}","compare_url":"https://api.github.com/repos/bem/gemini/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bem/gemini/merges","archive_url":"https://api.github.com/repos/bem/gemini/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bem/gemini/downloads","issues_url":"https://api.github.com/repos/bem/gemini/issues{/number}","pulls_url":"https://api.github.com/repos/bem/gemini/pulls{/number}","milestones_url":"https://api.github.com/repos/bem/gemini/milestones{/number}","notifications_url":"https://api.github.com/repos/bem/gemini/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bem/gemini/labels{/name}","releases_url":"https://api.github.com/repos/bem/gemini/releases{/id}","created_at":"2014-02-11T14:58:41Z","updated_at":"2014-10-14T08:31:49Z","pushed_at":"2014-10-16T09:31:51Z","git_url":"git://github.com/bem/gemini.git","ssh_url":"git@github.com:bem/gemini.git","clone_url":"https://github.com/bem/gemini.git","svn_url":"https://github.com/bem/gemini","homepage":null,"size":3686,"stargazers_count":112,"watchers_count":112,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"open_issues_count":26,"forks":13,"open_issues":26,"watchers":112,"default_branch":"master"}},"base":{"label":"bem:master","ref":"master","sha":"ec804f759f87ed0ac1042c7b5785a158d5573a0d","user":{"login":"bem","id":223412,"avatar_url":"https://avatars.githubusercontent.com/u/223412?v=2","gravatar_id":"","url":"https://api.github.com/users/bem","html_url":"https://github.com/bem","followers_url":"https://api.github.com/users/bem/followers","following_url":"https://api.github.com/users/bem/following{/other_user}","gists_url":"https://api.github.com/users/bem/gists{/gist_id}","starred_url":"https://api.github.com/users/bem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bem/subscriptions","organizations_url":"https://api.github.com/users/bem/orgs","repos_url":"https://api.github.com/users/bem/repos","events_url":"https://api.github.com/users/bem/events{/privacy}","received_events_url":"https://api.github.com/users/bem/received_events","type":"Organization","site_admin":false},"repo":{"id":16734168,"name":"gemini","full_name":"bem/gemini","owner":{"login":"bem","id":223412,"avatar_url":"https://avatars.githubusercontent.com/u/223412?v=2","gravatar_id":"","url":"https://api.github.com/users/bem","html_url":"https://github.com/bem","followers_url":"https://api.github.com/users/bem/followers","following_url":"https://api.github.com/users/bem/following{/other_user}","gists_url":"https://api.github.com/users/bem/gists{/gist_id}","starred_url":"https://api.github.com/users/bem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bem/subscriptions","organizations_url":"https://api.github.com/users/bem/orgs","repos_url":"https://api.github.com/users/bem/repos","events_url":"https://api.github.com/users/bem/events{/privacy}","received_events_url":"https://api.github.com/users/bem/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bem/gemini","description":"Utility for regression testing of web pages using screenshots","fork":false,"url":"https://api.github.com/repos/bem/gemini","forks_url":"https://api.github.com/repos/bem/gemini/forks","keys_url":"https://api.github.com/repos/bem/gemini/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bem/gemini/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bem/gemini/teams","hooks_url":"https://api.github.com/repos/bem/gemini/hooks","issue_events_url":"https://api.github.com/repos/bem/gemini/issues/events{/number}","events_url":"https://api.github.com/repos/bem/gemini/events","assignees_url":"https://api.github.com/repos/bem/gemini/assignees{/user}","branches_url":"https://api.github.com/repos/bem/gemini/branches{/branch}","tags_url":"https://api.github.com/repos/bem/gemini/tags","blobs_url":"https://api.github.com/repos/bem/gemini/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bem/gemini/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bem/gemini/git/refs{/sha}","trees_url":"https://api.github.com/repos/bem/gemini/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bem/gemini/statuses/{sha}","languages_url":"https://api.github.com/repos/bem/gemini/languages","stargazers_url":"https://api.github.com/repos/bem/gemini/stargazers","contributors_url":"https://api.github.com/repos/bem/gemini/contributors","subscribers_url":"https://api.github.com/repos/bem/gemini/subscribers","subscription_url":"https://api.github.com/repos/bem/gemini/subscription","commits_url":"https://api.github.com/repos/bem/gemini/commits{/sha}","git_commits_url":"https://api.github.com/repos/bem/gemini/git/commits{/sha}","comments_url":"https://api.github.com/repos/bem/gemini/comments{/number}","issue_comment_url":"https://api.github.com/repos/bem/gemini/issues/comments/{number}","contents_url":"https://api.github.com/repos/bem/gemini/contents/{+path}","compare_url":"https://api.github.com/repos/bem/gemini/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bem/gemini/merges","archive_url":"https://api.github.com/repos/bem/gemini/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bem/gemini/downloads","issues_url":"https://api.github.com/repos/bem/gemini/issues{/number}","pulls_url":"https://api.github.com/repos/bem/gemini/pulls{/number}","milestones_url":"https://api.github.com/repos/bem/gemini/milestones{/number}","notifications_url":"https://api.github.com/repos/bem/gemini/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bem/gemini/labels{/name}","releases_url":"https://api.github.com/repos/bem/gemini/releases{/id}","created_at":"2014-02-11T14:58:41Z","updated_at":"2014-10-14T08:31:49Z","pushed_at":"2014-10-16T09:31:51Z","git_url":"git://github.com/bem/gemini.git","ssh_url":"git@github.com:bem/gemini.git","clone_url":"https://github.com/bem/gemini.git","svn_url":"https://github.com/bem/gemini","homepage":null,"size":3686,"stargazers_count":112,"watchers_count":112,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":13,"mirror_url":null,"open_issues_count":26,"forks":13,"open_issues":26,"watchers":112,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bem/gemini/pulls/71"},"html":{"href":"https://github.com/bem/gemini/pull/71"},"issue":{"href":"https://api.github.com/repos/bem/gemini/issues/71"},"comments":{"href":"https://api.github.com/repos/bem/gemini/issues/71/comments"},"review_comments":{"href":"https://api.github.com/repos/bem/gemini/pulls/71/comments"},"review_comment":{"href":"https://api.github.com/repos/bem/gemini/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/bem/gemini/pulls/71/commits"},"statuses":{"href":"https://api.github.com/repos/bem/gemini/statuses/7c0a4f69631aa349c6b79a46cb69767cdba3038b"}}}}