[Pkg-javascript-commits] [node-coveralls] 10/332: changed to use lcov input format only.

Bastien Roucariès rouca at moszumanska.debian.org
Thu Nov 9 13:53:33 UTC 2017


This is an automated email from the git hooks/post-receive script.

rouca pushed a commit to branch master
in repository node-coveralls.

commit 112119e43cb048cfa0dbd98d6e03833b8ca4b619
Author: cainus <gregg at caines.ca>
Date:   Wed Mar 27 23:48:04 2013 -0700

    changed to use lcov input format only.
---
 .travis.yml                    |   3 +
 bin/coveralls.js               | 141 +++-----------------------
 fixtures/lib/index.js          | 224 +++++++++++++++++++++++++++++++++++++++++
 fixtures/onefile.lcov          | 116 +++++++++++++++++++++
 lib/convertLcovToCoveralls.js  |  90 +++++++++++++++++
 lib/parser.js                  | 131 ++++++++++++++++++++++++
 lib/sendToCoveralls.js         |  11 ++
 package.json                   |   6 +-
 test/convertLcovToCoveralls.js |  27 +++++
 9 files changed, 616 insertions(+), 133 deletions(-)

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..debfa19
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+  - 0.8
diff --git a/bin/coveralls.js b/bin/coveralls.js
index 37ea880..7769a0f 100644
--- a/bin/coveralls.js
+++ b/bin/coveralls.js
@@ -1,149 +1,32 @@
 #!/usr/bin/env node
-
-var http = require('http');
-var request = require('request');
-var FormData = require('form-data');
-var TRAVIS_JOB_ID = process.env.TRAVIS_JOB_ID || 'unknown';
+var sendToCoveralls = require('../lib/sendToCoveralls');
+var convertLcovToCoveralls = require('../lib/convertLcovToCoveralls');
 
 process.stdin.resume();
 process.stdin.setEncoding('utf8');
 
-var inJson = '';
+var input = '';
 
 process.stdin.on('data', function(chunk) {
-	inJson += chunk;
+	input += chunk;
 });
 
 process.stdin.on('end', function() {
-	reportToCoveralls(inJson);
+	inputToCoveralls(input);
 });
 
-// cleans off any leading / trailing non-json garbage
-var trimToJson = function(inJson){
-	inJson = inJson.replace(/^[^\{]*/, '');
-	inJson = inJson.replace(/\}[^\}]*$/, '}');
-  return inJson;
-};
-
-var convertCoverageValue = function(val){
-	if (val === ""){
-		return null;
-	}
-	if (val > 0){
-		return 1;
-	}
-	return 0;
-};
-
-var convertFileObject = function(file){
-	var source = '';
-	var coverage = [];
-	for (var lineNumber in file.source){
-		source += file.source[lineNumber].source + "\n";
-		coverage.push(convertCoverageValue(
-											file.source[lineNumber].coverage));
-	}
-	return { name     : file.filename,
-           source   : source,
-           coverage : coverage	};
-};
-
-var convertJsonCovToCoveralls = function(data){
-	var files = data.files;
-	var postJson = {
-    service_job_id : TRAVIS_JOB_ID,
-    service_name : "travis-ci",
-		source_files : []
-	};
-	files.forEach(function(file){
-		postJson.source_files.push(convertFileObject(file));
-	});
-	return postJson;
-};
-
-var sendToCoveralls = function(postJson){
-  var str = JSON.stringify(postJson);
-  var url = 'https://coveralls.io/api/v1/jobs';
-  request({url : url, method : 'POST', form : { json : str}}, function(err, response, body){
+var inputToCoveralls = function(input){
+	console.log(input);
+	var postData = convertLcovToCoveralls(input);
+  sendToCoveralls(postData, function(err, response, body){
     if (err){
       throw err;
     }
+    if (response.statusCode >= 400){
+      throw "Bad response: " + response.statusCode + " " + body;
+    }
     console.log(response.statusCode);
     console.log(body);
   });
-};
-
 
-var reportToCoveralls = function(inJson){
-	inJson = trimToJson(inJson);
-	var data = JSON.parse(inJson);
-	console.log("successfully read json from json-cov.");
-	postJson = convertJsonCovToCoveralls(data);
-	console.log(JSON.stringify(postJson));
-	console.log("successfully converted input json to coveralls format.");
-  sendToCoveralls(postJson);
 };
-
-
-/* example coveralls json file
-
-
-{
-  "service_job_id": "1234567890",
-  "service_name": "travis-ci",
-  "source_files": [
-    {
-      "name": "example.rb",
-      "source": "def four\n  4\nend",
-      "coverage": [null, 1, null]
-    },
-    {
-      "name": "two.rb",
-      "source": "def seven\n  eight\n  nine\nend",
-      "coverage": [null, 1, 0, null]
-    }
-  ]
-}
-
-
-*/
-
-/*  example json-cov file
-
-{
-      "filename": "CRUDCollection.js",
-      "coverage": 94.20289855072464,
-      "hits": 65,
-      "misses": 4,
-      "sloc": 69,
-      "source": {
-        "1": {
-          "source": "var JSV = require('JSV').JSV;",
-          "coverage": 1
-        },
-        "2": {
-          "source": "var _ = require('underscore');",
-          "coverage": 1
-        },
-        "3": {
-          "source": "",
-          "coverage": ""
-        },
-        "4": {
-          "source": "var CRUDCollection = function(options){",
-          "coverage": 1
-        },
-        "5": {
-          "source": "",
-          "coverage": ""
-        },
-        "6": {
-          "source": "  if (!options || (!options.list && !options.collectionGET)){",
-          "coverage": 24
-        },
-
-
-
-
-
-*/
diff --git a/fixtures/lib/index.js b/fixtures/lib/index.js
new file mode 100644
index 0000000..4a22c7a
--- /dev/null
+++ b/fixtures/lib/index.js
@@ -0,0 +1,224 @@
+var nodeUrl = require('url');
+var querystring = require('querystring');
+var _ = require('underscore');
+
+var UrlGrey = function(url){
+  this.url = url;
+  this._parsed = null;
+};
+
+UrlGrey.prototype.parsed = function(){
+  if (!this._parsed){
+    this._parsed = nodeUrl.parse(this.url);
+    var p = this._parsed;
+    if (p.protocol){
+      p.protocol = p.protocol.slice(0,-1);
+    } else {
+      p.protocol = 'http';
+    }
+    if (p.hash){
+      p.hash = p.hash.substring(1);
+    }
+    p.username = ''; 
+    p.password = '';
+    if (!p.hostname){
+      p.hostname = 'localhost';
+    }
+    if (!p.port){
+      p.port = 80;
+    } else {
+      p.port = parseInt(p.port, 10);
+    }
+    if (p.auth){
+      var auth = p.auth.split(':');
+      p.username = auth[0]; 
+      p.password = auth[1];
+    } 
+  }
+  return this._parsed;
+};
+
+UrlGrey.prototype.query = function(mergeObject){
+  var path;
+  if (mergeObject === false){
+    // clear the query entirely if the input === false
+    return this.queryString('');
+  }
+  
+  var url = this.url;
+  if (!mergeObject){
+    var parsed = nodeUrl.parse(url);
+    if (!!parsed.search){
+      var qstr = parsed.search.substring(1);
+      return querystring.parse(qstr);
+    }
+    return {};
+  } else {
+    // read the object out
+    var oldQuery = querystring.parse(this.queryString());
+    _.each(mergeObject, function(v, k){
+      if (v === null){
+        delete oldQuery[k];
+      } else {
+        oldQuery[k] = v;
+      }
+    });
+    var newString = querystring.stringify(oldQuery, '&', '=');
+    return this.queryString(newString);
+  }
+};
+
+
+addPropertyGetterSetter('protocol');
+addPropertyGetterSetter('port');
+addPropertyGetterSetter('username');
+addPropertyGetterSetter('password');
+addPropertyGetterSetter('hostname');
+addPropertyGetterSetter('hash');
+// add a method called queryString that manipulates 'query'
+addPropertyGetterSetter('query', 'queryString');  
+addPropertyGetterSetter('pathname', 'path');  
+
+UrlGrey.prototype.path = function(){
+  var args = _.toArray(arguments);
+  if (args.length !== 0){
+    var obj = new UrlGrey(this.toString());
+    var str = _.flatten(args).join('/');
+    str = str.replace(/\/+/g, '/'); // remove double slashes
+    str = str.replace(/\/$/, '');  // remove all trailing slashes
+    if (str[0] !== '/'){ str = '/' + str; }
+    obj.parsed().pathname = str;
+    return obj;
+  }
+  return this.parsed().pathname;
+};
+
+
+UrlGrey.prototype.encode = function(str){
+  return querystring.escape(str);
+};
+
+UrlGrey.prototype.decode = function(str){
+  return querystring.unescape(str);
+};
+
+UrlGrey.prototype.parent = function(){
+  // read-only.  (can't SET parent)
+  var pieces = this.path().split("/");
+  var popped = pieces.pop();
+  if (popped === ''){  // ignore trailing slash
+    pieces.pop();
+  }
+  return this.path(pieces.join("/"));
+};
+
+UrlGrey.prototype.child = function(suffix){
+  if (suffix){
+    suffix = encodeURIComponent(suffix);
+    return this.path(this.path(), suffix);
+  } else {
+    // if no suffix, return the child
+    var pieces = this.path().split("/");
+    var last = _.last(pieces);
+    if ((pieces.length > 1) && (last === '')){
+      // ignore trailing slashes
+      pieces.pop();
+      last = _.last(pieces);
+    }
+    return last;
+  }
+};
+
+UrlGrey.prototype.toJSON = function(){
+  return this.toString();
+};
+
+UrlGrey.prototype.toString = function(){
+  var p = this.parsed();
+  var userinfo = p.username + ':' + p.password;
+  var retval = this.protocol() + '://';
+  if (userinfo != ':'){
+    retval += userinfo + '@';
+  }
+  retval += p.hostname;
+  if (this.port() !== 80){
+    retval += ':' + this.port();
+  }
+  retval += this.path() === '/' ? '' : this.path();
+  var qs = this.queryString();
+  if (qs){
+    retval += '?' + qs;
+  }
+  if (p.hash){
+    retval += '#' + p.hash;
+  }
+  return retval;
+};
+
+/*
+UrlGrey.prototype.absolute = function(path){
+  if (path[0] == '/'){
+    path = path.substring(1);
+  }
+  var parsed = nodeUrl.parse(path);
+  if (!!parsed.protocol){  // if it's already absolute, just return it
+    return path;
+  }
+  return this._protocol + "://" + this._host + '/' + path;
+};
+
+// TODO make this interpolate vars into the url.   both sinatra style and url-tempates
+// TODO name this: 
+UrlGrey.prototype.get = function(nameOrPath, varDict){
+  if (!!nameOrPath){
+    if (!!varDict){
+      return this.absolute(this._router.getUrl(nameOrPath, varDict));
+    }
+    return this.absolute(this._router.getUrl(nameOrPath));
+  }
+  return this.url;
+};*/
+
+/*
+// TODO needs to take a template as an input
+UrlGrey.prototype.param = function(key, defaultValue){
+  var value = this.params()[key];
+  if (!!value) { 
+    return value; 
+  }
+  return defaultValue;
+};
+
+// TODO extract params, given a template?
+// TODO needs to take a template as an input
+UrlGrey.prototype.params = function(inUrl){
+  if (!!inUrl){
+    return this._router.pathVariables(inUrl);
+  }
+  if (!!this._params){
+    return this._params;
+  }
+  return this._router.pathVariables(this.url);
+};
+*/
+
+// TODO relative()  // takes an absolutepath and returns a relative one
+// TODO absolute() // takes a relative path and returns an absolute one.
+
+
+
+module.exports = function(url){ return new UrlGrey(url); };
+
+function addPropertyGetterSetter(propertyName, methodName){
+  if (!methodName){
+    methodName = propertyName;
+  }
+  UrlGrey.prototype[methodName] = function(str){
+    if (!!str || str === ''){
+      var obj = new UrlGrey(this.toString());
+      obj.parsed()[propertyName] = str;
+      return obj;
+    }
+    return this.parsed()[propertyName];  
+  };
+}
diff --git a/fixtures/onefile.lcov b/fixtures/onefile.lcov
new file mode 100644
index 0000000..bc24682
--- /dev/null
+++ b/fixtures/onefile.lcov
@@ -0,0 +1,116 @@
+make[1]: Entering directory `/home/cainus/urlgrey'
+SF:index.js
+DA:1,1
+DA:2,1
+DA:3,1
+DA:5,1
+DA:6,66
+DA:7,66
+DA:10,1
+DA:11,323
+DA:12,63
+DA:13,63
+DA:14,63
+DA:15,60
+DA:17,3
+DA:19,63
+DA:20,32
+DA:22,63
+DA:23,63
+DA:24,63
+DA:25,3
+DA:27,63
+DA:28,60
+DA:30,3
+DA:32,63
+DA:33,27
+DA:34,27
+DA:35,27
+DA:38,323
+DA:41,1
+DA:42,5
+DA:43,5
+DA:45,2
+DA:48,3
+DA:49,3
+DA:50,1
+DA:51,1
+DA:52,1
+DA:53,1
+DA:55,0
+DA:58,2
+DA:59,2
+DA:60,2
+DA:61,0
+DA:63,2
+DA:66,2
+DA:67,2
+DA:72,1
+DA:73,1
+DA:74,1
+DA:75,1
+DA:76,1
+DA:77,1
+DA:79,1
+DA:80,1
+DA:82,1
+DA:83,87
+DA:84,87
+DA:85,6
+DA:86,6
+DA:87,6
+DA:88,6
+DA:89,9
+DA:90,6
+DA:91,6
+DA:93,81
+DA:97,1
+DA:98,1
+DA:101,1
+DA:102,1
+DA:105,1
+DA:107,2
+DA:108,2
+DA:109,2
+DA:110,1
+DA:112,2
+DA:115,1
+DA:116,3
+DA:117,1
+DA:118,1
+DA:121,2
+DA:122,2
+DA:123,2
+DA:125,1
+DA:126,1
+DA:128,2
+DA:132,1
+DA:133,1
+DA:136,1
+DA:137,50
+DA:138,50
+DA:139,50
+DA:140,50
+DA:141,20
+DA:143,50
+DA:144,50
+DA:145,2
+DA:147,50
+DA:148,50
+DA:149,50
+DA:150,31
+DA:152,50
+DA:153,24
+DA:155,50
+DA:210,40
+DA:212,1
+DA:213,8
+DA:214,6
+DA:216,8
+DA:217,186
+DA:218,21
+DA:219,21
+DA:220,21
+DA:222,165
+end_of_record
+make[1]: Leaving directory `/home/cainus/urlgrey'
diff --git a/lib/convertLcovToCoveralls.js b/lib/convertLcovToCoveralls.js
new file mode 100644
index 0000000..cd3ee6d
--- /dev/null
+++ b/lib/convertLcovToCoveralls.js
@@ -0,0 +1,90 @@
+var TRAVIS_JOB_ID = process.env.TRAVIS_JOB_ID || 'unknown';
+var fs = require('fs');
+var lcovParse = require('./parser');
+
+var detailsToCoverage = function(length, details){
+  var coverage = new Array(length);
+  details.forEach(function(obj){
+    coverage[obj.line] = obj.hit;
+  });
+  return coverage;
+};
+
+var convertLcovFileObject = function(file, filepath){
+	var path = filepath + "/" + file.file;
+	var source = fs.readFileSync(path, 'utf8');
+	var lines = source.split("\n");
+	var coverage = detailsToCoverage(lines.length, file.lines.details);
+	return { name     : file.file,
+           source   : source,
+           coverage : coverage	};
+};
+
+var convertLcovToCoveralls = function(input, filepath){
+  filepath = filepath || 'lib';
+  if (filepath[0] !== '/'){
+    filepath = process.cwd() + '/' + filepath;
+  }
+  var parsed = lcovParse(input);
+	var postJson = {
+    service_job_id : TRAVIS_JOB_ID,
+    service_name : "travis-ci",
+		source_files : []
+	};
+	parsed.forEach(function(file){
+		postJson.source_files.push(convertLcovFileObject(file, filepath));
+	});
+	return postJson;
+};
+
+module.exports = convertLcovToCoveralls;
+
+/* example coveralls json file
+
+
+{
+  "service_job_id": "1234567890",
+  "service_name": "travis-ci",
+  "source_files": [
+    {
+      "name": "example.rb",
+      "source": "def four\n  4\nend",
+      "coverage": [null, 1, null]
+    },
+    {
+      "name": "two.rb",
+      "source": "def seven\n  eight\n  nine\nend",
+      "coverage": [null, 1, 0, null]
+    }
+  ]
+}
+
+
+example output from lcov parser:
+
+ [
+  {
+    "file": "index.js",
+    "lines": {
+      "found": 0,
+      "hit": 0,
+      "details": [
+        {
+          "line": 1,
+          "hit": 1
+        },
+        {
+          "line": 2,
+          "hit": 1
+        },
+        {
+          "line": 3,
+          "hit": 1
+        },
+        {
+          "line": 5,
+          "hit": 1
+        },
+
+*/
+
diff --git a/lib/parser.js b/lib/parser.js
new file mode 100644
index 0000000..83b2c3d
--- /dev/null
+++ b/lib/parser.js
@@ -0,0 +1,131 @@
+/*
+Software License Agreement (BSD License)
+
+Copyright (c) 2012, Dav Glass <davglass at gmail.com>.
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* The name of Dav Glass may not be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of Dav Glass.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+var lcovParse = function(str) {
+    var data = [], item = {};
+
+    str = str.split('\n');
+    str.forEach(function(line) {
+        line = line.trim();
+
+        var parts = line.split(':'), lines, fn;
+
+        switch (parts[0].toUpperCase()) {
+            case 'TN':
+                item.title = parts[1].trim();
+                break;
+            case 'SF':
+                item.file = parts[1].trim();
+                break;
+            case 'FNF':
+                item.functions.found = Number(parts[1].trim());
+                break;
+            case 'FNH':
+                item.functions.hit = Number(parts[1].trim());
+                break;
+            case 'LF':
+                item.lines.found = Number(parts[1].trim());
+                break;
+            case 'LH':
+                item.lines.hit = Number(parts[1].trim());
+                break;
+            case 'DA':
+                if (!item.lines) {
+                    item.lines = {
+                        found: 0,
+                        hit: 0,
+                        details: []
+                    };
+                }
+                lines = parts[1].split(',');
+                item.lines.details.push({
+                    line: Number(lines[0]),
+                    hit: Number(lines[1])
+                });
+                break;
+            case 'FN':
+                if (!item.functions) {
+                    item.functions = {
+                        hit: 0,
+                        found: 0,
+                        details: []
+                    };
+                }
+                fn = parts[1].split(',');
+                item.functions.details.push({
+                    name: fn[1],
+                    line: Number(fn[0])
+                });
+                break;
+            case 'FNDA':
+                fn = parts[1].split(',');
+                item.functions.details.some(function(i, k) {
+                    if (i.name === fn[1] && i.hit === undefined) {
+                        item.functions.details[k].hit = Number(fn[0]);
+                        return true;
+                    }
+                });
+                break;
+            case 'BRDA':
+                if (!item.branches) {
+                    item.branches = {
+                        hit: 0,
+                        found: 0,
+                        details: []
+                    };
+                }
+                fn = parts[1].split(',');
+                item.branches.details.push({
+                    line: Number(fn[0]),
+                    block: Number(fn[1]),
+                    branch: Number(fn[2]),
+                    taken: ((fn[3] === '-') ? 0 : Number(fn[3]))
+                });
+                break;
+            case 'BRF':
+                item.branches.found = Number(parts[1]);
+                break;
+            case 'BRH':
+                item.branches.hit = Number(parts[1]);
+                break;
+        }
+
+        if (line.indexOf('end_of_record') > -1) {
+            data.push(item);
+            item = {};
+        }
+    });
+    return data;
+};
+
+module.exports = lcovParse;
diff --git a/lib/sendToCoveralls.js b/lib/sendToCoveralls.js
new file mode 100644
index 0000000..b3514c6
--- /dev/null
+++ b/lib/sendToCoveralls.js
@@ -0,0 +1,11 @@
+var request = require('request');
+
+var sendToCoveralls = function(obj, cb){
+  var str = JSON.stringify(obj);
+  var url = 'https://coveralls.io/api/v1/jobs';
+  request({url : url, method : 'POST', form : { json : str}}, function(err, response, body){
+    cb(err, response, body);
+  });
+};
+
+module.exports = sendToCoveralls;
diff --git a/package.json b/package.json
index 0fc55d9..cb37c26 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
       "name": "coveralls",
       "description" : "takes json-cov output into stdin and POSTs to coveralls.io",
       "keywords" : ["coverage", "coveralls"], 
-      "version": "1.1.3",
+      "version": "2.0.0",
       "bugs": {
        "url": "https://github.com/cainus/node-coveralls/issues"
       },
@@ -20,9 +20,7 @@
         } 
       ],
       "dependencies": {
-          "underscore" : "1.3.3",
-          "request" : "2.16.2",
-          "form-data" : "0.0.7"
+          "request" : "2.16.2"
       },
       "devDependencies" : {
           "mocha" : "1.8.1",
diff --git a/test/convertLcovToCoveralls.js b/test/convertLcovToCoveralls.js
new file mode 100644
index 0000000..df8c5cf
--- /dev/null
+++ b/test/convertLcovToCoveralls.js
@@ -0,0 +1,27 @@
+var convertLcovToCoveralls = require('../lib/convertLcovToCoveralls');
+var should = require('should');
+var fs = require('fs');
+
+describe("convertLcovToCoveralls", function(){
+  it ("should convert a simple lcov file", function(){
+    process.env.TRAVIS_JOB_ID = -1;
+    var path = __dirname + "/../fixtures/onefile.lcov";
+    var input = fs.readFileSync(path, "utf8");
+    var libpath = __dirname + "/../fixtures/lib";
+    var output = convertLcovToCoveralls(input, libpath);
+    output.source_files[0].name.should.equal("index.js");
+    output.source_files[0].source.split("\n").length.should.equal(225);
+    output.source_files[0].coverage[55].should.equal(0);
+    output.source_files[0].coverage[61].should.equal(0);
+  });
+
+  it ("should work with a relative path as well", function(){
+    process.env.TRAVIS_JOB_ID = -1;
+    var path = __dirname + "/../fixtures/onefile.lcov";
+    var input = fs.readFileSync(path, "utf8");
+    var libpath = "fixtures/lib";
+    var output = convertLcovToCoveralls(input, libpath);
+    output.source_files[0].name.should.equal("index.js");
+    output.source_files[0].source.split("\n").length.should.equal(225);
+  });
+});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-coveralls.git



More information about the Pkg-javascript-commits mailing list