[Pkg-javascript-commits] [node-rollup-pluginutils] 01/03: New upstream version 2.0.1
Julien Puydt
julien.puydt at laposte.net
Wed Jul 5 17:42:40 UTC 2017
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-rollup-pluginutils.
commit 7a96d2d066c39d37f0bbc4cf57939904cf560ba2
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Wed Jul 5 18:20:27 2017 +0200
New upstream version 2.0.1
---
.eslintrc | 24 ++++++
.gitignore | 4 +
.travis.yml | 10 +++
CHANGELOG.md | 51 ++++++++++++
README.md | 109 +++++++++++++++++++++++++
appveyor.yml | 31 +++++++
package.json | 40 +++++++++
rollup.config.js | 19 +++++
src/addExtension.js | 6 ++
src/attachScopes.js | 147 +++++++++++++++++++++++++++++++++
src/createFilter.js | 33 ++++++++
src/index.js | 4 +
src/makeLegalIdentifier.js | 15 ++++
src/utils/ensureArray.js | 5 ++
test/test.js | 198 +++++++++++++++++++++++++++++++++++++++++++++
15 files changed, 696 insertions(+)
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..c6338cc
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,24 @@
+{
+ "rules": {
+ "indent": [ 2, "tab", { "SwitchCase": 1 } ],
+ "quotes": [ 2, "single" ],
+ "linebreak-style": [ 2, "unix" ],
+ "semi": [ 2, "always" ],
+ "space-after-keywords": [ 2, "always" ],
+ "space-before-blocks": [ 2, "always" ],
+ "space-before-function-paren": [ 2, "always" ],
+ "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
+ "no-cond-assign": [ 0 ]
+ },
+ "env": {
+ "es6": true,
+ "browser": true,
+ "mocha": true,
+ "node": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 8,
+ "sourceType": "module"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..26ad730
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.DS_Store
+node_modules
+dist
+.gobble*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..9a040ed
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+sudo: false
+language: node_js
+node_js:
+ - "0.12"
+ - "4"
+env:
+ global:
+ - BUILD_TIMEOUT=10000
+install: npm install
+# script: npm run ci
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..1100d0e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,51 @@
+# rollup-pluginutils changelog
+
+## 2.0.1
+
+* Don't add extension to file with trailing dot ([#14](https://github.com/rollup/rollup-pluginutils/issues/14))
+
+## 2.0.0
+
+* Use `micromatch` instead of `minimatch` ([#19](https://github.com/rollup/rollup-pluginutils/issues/19))
+* Allow `createFilter` to take regexes ([#5](https://github.com/rollup/rollup-pluginutils/issues/5))
+
+## 1.5.2
+
+* Treat `arguments` as a reserved word ([#10](https://github.com/rollup/rollup-pluginutils/issues/10))
+
+## 1.5.1
+
+* Add all declarators in a var declaration to scope, not just the first
+
+## 1.5.0
+
+* Exclude IDs with null character (`\0`)
+
+## 1.4.0
+
+* Workaround minimatch issue ([#6](https://github.com/rollup/rollup-pluginutils/pull/6))
+* Exclude non-string IDs in `createFilter`
+
+## 1.3.1
+
+* Build with Rollup directly, rather than via Gobble
+
+## 1.3.0
+
+* Use correct path separator on Windows
+
+## 1.2.0
+
+* Add `attachScopes` and `makeLegalIdentifier`
+
+## 1.1.0
+
+* Add `addExtension` function
+
+## 1.0.1
+
+* Include dist files in package
+
+## 1.0.0
+
+* First release
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1a6126d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,109 @@
+# rollup-pluginutils
+
+A set of functions commonly used by Rollup plugins.
+
+
+## Installation
+
+```bash
+npm install --save rollup-pluginutils
+```
+
+
+## Usage
+
+### addExtension
+
+```js
+import { addExtension } from 'rollup-pluginutils';
+
+export default function myPlugin ( options = {} ) {
+ return {
+ resolveId ( code, id ) {
+ // only adds an extension if there isn't one already
+ id = addExtension( id ); // `foo` -> `foo.js`, `foo.js -> foo.js`
+ id = addExtension( id, '.myext' ); // `foo` -> `foo.myext`, `foo.js -> `foo.js`
+ }
+ };
+}
+```
+
+
+### attachScopes
+
+This function attaches `Scope` objects to the relevant nodes of an AST. Each `Scope` object has a `scope.contains(name)` method that returns `true` if a given name is defined in the current scope or a parent scope.
+
+See [rollup-plugin-inject](https://github.com/rollup/rollup-plugin-inject) or [rollup-plugin-commonjs](https://github.com/rollup/rollup-plugin-inject) for an example of usage.
+
+```js
+import { attachScopes } from 'rollup-pluginutils';
+import { parse } from 'acorn';
+import { walk } from 'estree-walker';
+
+export default function myPlugin ( options = {} ) {
+ return {
+ transform ( code ) {
+ const ast = parse( ast, {
+ ecmaVersion: 6,
+ sourceType: 'module'
+ });
+
+ let scope = attachScopes( ast, 'scope' );
+
+ walk( ast, {
+ enter ( node ) {
+ if ( node.scope ) scope = node.scope;
+
+ if ( !scope.contains( 'foo' ) ) {
+ // `foo` is not defined, so if we encounter it,
+ // we assume it's a global
+ }
+ },
+ leave ( node ) {
+ if ( node.scope ) scope = scope.parent;
+ }
+ });
+ }
+ };
+}
+```
+
+
+### createFilter
+
+```js
+import { createFilter } from 'rollup-pluginutils';
+
+export default function myPlugin ( options = {} ) {
+ // `options.include` and `options.exclude` can each be a minimatch
+ // pattern, or an array of minimatch patterns, relative to process.cwd()
+ var filter = createFilter( options.include, options.exclude );
+
+ return {
+ transform ( code, id ) {
+ // if `options.include` is omitted or has zero length, filter
+ // will return `true` by default. Otherwise, an ID must match
+ // one or more of the minimatch patterns, and must not match
+ // any of the `options.exclude` patterns.
+ if ( !filter( id ) ) return;
+
+ // proceed with the transformation...
+ }
+ };
+}
+```
+
+
+### makeLegalIdentifier
+
+```js
+import { makeLegalIdentifier } from 'rollup-pluginutils';
+
+makeLegalIdentifier( 'foo-bar' ); // 'foo_bar'
+makeLegalIdentifier( 'typeof' ); // '_typeof'
+```
+
+
+## License
+
+MIT
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..6877afb
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,31 @@
+# http://www.appveyor.com/docs/appveyor-yml
+
+version: "{build}"
+
+clone_depth: 10
+
+init:
+ - git config --global core.autocrlf false
+
+environment:
+ matrix:
+ # node.js
+ - nodejs_version: 0.12
+ - nodejs_version: 4
+
+install:
+ - ps: Install-Product node $env:nodejs_version
+ - npm install
+
+build: off
+
+test_script:
+ - node --version && npm --version
+ - npm test
+
+matrix:
+ fast_finish: false
+
+# cache:
+# - C:\Users\appveyor\AppData\Roaming\npm-cache -> package.json # npm cache
+# - node_modules -> package.json # local npm modules
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..edc5b09
--- /dev/null
+++ b/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "rollup-pluginutils",
+ "description": "Functionality commonly needed by Rollup plugins",
+ "version": "2.0.1",
+ "main": "dist/pluginutils.cjs.js",
+ "module": "dist/pluginutils.es.js",
+ "jsnext:main": "dist/pluginutils.es.js",
+ "files": [
+ "src",
+ "dist",
+ "README.md"
+ ],
+ "devDependencies": {
+ "eslint": "^3.12.2",
+ "mocha": "^3.2.0",
+ "rollup": "^0.40.0",
+ "rollup-plugin-buble": "^0.15.0"
+ },
+ "scripts": {
+ "test": "mocha",
+ "build": "rollup -c",
+ "pretest": "npm run build",
+ "prepublish": "npm test"
+ },
+ "dependencies": {
+ "estree-walker": "^0.3.0",
+ "micromatch": "^2.3.11"
+ },
+ "repository": "rollup/rollup-pluginutils",
+ "keywords": [
+ "rollup",
+ "utils"
+ ],
+ "author": "Rich Harris <richard.a.harris at gmail.com>",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/rollup/rollup-pluginutils/issues"
+ },
+ "homepage": "https://github.com/rollup/rollup-pluginutils#readme"
+}
diff --git a/rollup.config.js b/rollup.config.js
new file mode 100644
index 0000000..a2685c7
--- /dev/null
+++ b/rollup.config.js
@@ -0,0 +1,19 @@
+var pkg = require('./package.json');
+import buble from 'rollup-plugin-buble';
+
+export default {
+ entry: 'src/index.js',
+ plugins: [ buble() ],
+ external: [ 'path', 'estree-walker', 'micromatch' ],
+
+ targets: [
+ {
+ format: 'cjs',
+ dest: pkg['main']
+ },
+ {
+ format: 'es',
+ dest: pkg['module']
+ }
+ ]
+};
diff --git a/src/addExtension.js b/src/addExtension.js
new file mode 100644
index 0000000..aefa63e
--- /dev/null
+++ b/src/addExtension.js
@@ -0,0 +1,6 @@
+import { extname } from 'path';
+
+export default function addExtension ( filename, ext = '.js' ) {
+ if ( !extname( filename ) ) filename += ext;
+ return filename;
+}
diff --git a/src/attachScopes.js b/src/attachScopes.js
new file mode 100644
index 0000000..d2d1409
--- /dev/null
+++ b/src/attachScopes.js
@@ -0,0 +1,147 @@
+import { walk } from 'estree-walker';
+
+const blockDeclarations = {
+ 'const': true,
+ 'let': true
+};
+
+const extractors = {
+ Identifier ( names, param ) {
+ names.push( param.name );
+ },
+
+ ObjectPattern ( names, param ) {
+ param.properties.forEach( prop => {
+ extractors[ prop.key.type ]( names, prop.key );
+ });
+ },
+
+ ArrayPattern ( names, param ) {
+ param.elements.forEach( element => {
+ if ( element ) extractors[ element.type ]( names, element );
+ });
+ },
+
+ RestElement ( names, param ) {
+ extractors[ param.argument.type ]( names, param.argument );
+ },
+
+ AssignmentPattern ( names, param ) {
+ return extractors[ param.left.type ]( names, param.left );
+ }
+};
+
+function extractNames ( param ) {
+ let names = [];
+
+ extractors[ param.type ]( names, param );
+ return names;
+}
+
+class Scope {
+ constructor ( options ) {
+ options = options || {};
+
+ this.parent = options.parent;
+ this.isBlockScope = !!options.block;
+
+ this.declarations = Object.create( null );
+
+ if ( options.params ) {
+ options.params.forEach( param => {
+ extractNames( param ).forEach( name => {
+ this.declarations[ name ] = true;
+ });
+ });
+ }
+ }
+
+ addDeclaration ( node, isBlockDeclaration, isVar ) {
+ if ( !isBlockDeclaration && this.isBlockScope ) {
+ // it's a `var` or function node, and this
+ // is a block scope, so we need to go up
+ this.parent.addDeclaration( node, isBlockDeclaration, isVar );
+ } else {
+ extractNames( node.id ).forEach( name => {
+ this.declarations[ name ] = true;
+ });
+ }
+ }
+
+ contains ( name ) {
+ return this.declarations[ name ] ||
+ ( this.parent ? this.parent.contains( name ) : false );
+ }
+}
+
+
+export default function attachScopes ( ast, propertyName = 'scope' ) {
+ let scope = new Scope();
+
+ walk( ast, {
+ enter ( node, parent ) {
+ // function foo () {...}
+ // class Foo {...}
+ if ( /(Function|Class)Declaration/.test( node.type ) ) {
+ scope.addDeclaration( node, false, false );
+ }
+
+ // var foo = 1
+ if ( node.type === 'VariableDeclaration' ) {
+ const isBlockDeclaration = blockDeclarations[ node.kind ];
+
+ node.declarations.forEach( declaration => {
+ scope.addDeclaration( declaration, isBlockDeclaration, true );
+ });
+ }
+
+ let newScope;
+
+ // create new function scope
+ if ( /Function/.test( node.type ) ) {
+ newScope = new Scope({
+ parent: scope,
+ block: false,
+ params: node.params
+ });
+
+ // named function expressions - the name is considered
+ // part of the function's scope
+ if ( node.type === 'FunctionExpression' && node.id ) {
+ newScope.addDeclaration( node, false, false );
+ }
+ }
+
+ // create new block scope
+ if ( node.type === 'BlockStatement' && !/Function/.test( parent.type ) ) {
+ newScope = new Scope({
+ parent: scope,
+ block: true
+ });
+ }
+
+ // catch clause has its own block scope
+ if ( node.type === 'CatchClause' ) {
+ newScope = new Scope({
+ parent: scope,
+ params: [ node.param ],
+ block: true
+ });
+ }
+
+ if ( newScope ) {
+ Object.defineProperty( node, propertyName, {
+ value: newScope,
+ configurable: true
+ });
+
+ scope = newScope;
+ }
+ },
+ leave ( node ) {
+ if ( node[ propertyName ] ) scope = scope.parent;
+ }
+ });
+
+ return scope;
+}
diff --git a/src/createFilter.js b/src/createFilter.js
new file mode 100644
index 0000000..c9bbe47
--- /dev/null
+++ b/src/createFilter.js
@@ -0,0 +1,33 @@
+import { resolve, sep } from 'path';
+import mm from 'micromatch';
+import ensureArray from './utils/ensureArray';
+
+export default function createFilter ( include, exclude ) {
+ const getMatcher = id => ( isRegexp( id ) ? id : { test: mm.matcher( resolve( id ) ) } );
+ include = ensureArray( include ).map( getMatcher );
+ exclude = ensureArray( exclude ).map( getMatcher );
+
+ return function ( id ) {
+
+ if ( typeof id !== 'string' ) return false;
+ if ( /\0/.test( id ) ) return false;
+
+ id = id.split( sep ).join( '/' );
+
+ for ( let i = 0; i < exclude.length; ++i ) {
+ const matcher = exclude[i];
+ if ( matcher.test( id ) ) return false;
+ }
+
+ for ( let i = 0; i < include.length; ++i ) {
+ const matcher = include[i];
+ if ( matcher.test( id ) ) return true;
+ }
+
+ return !include.length;
+ };
+}
+
+function isRegexp ( val ) {
+ return val instanceof RegExp;
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..fdce1bb
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,4 @@
+export { default as addExtension } from './addExtension';
+export { default as attachScopes } from './attachScopes';
+export { default as createFilter } from './createFilter';
+export { default as makeLegalIdentifier } from './makeLegalIdentifier';
diff --git a/src/makeLegalIdentifier.js b/src/makeLegalIdentifier.js
new file mode 100644
index 0000000..2fbb10b
--- /dev/null
+++ b/src/makeLegalIdentifier.js
@@ -0,0 +1,15 @@
+const reservedWords = 'break case class catch const continue debugger default delete do else export extends finally for function if import in instanceof let new return super switch this throw try typeof var void while with yield enum await implements package protected static interface private public'.split( ' ' );
+const builtins = 'arguments Infinity NaN undefined null true false eval uneval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Symbol Error EvalError InternalError RangeError ReferenceError SyntaxError TypeError URIError Number Math Date String RegExp Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array Map Set WeakMap WeakSet SIMD ArrayBuff [...]
+
+let blacklisted = Object.create( null );
+reservedWords.concat( builtins ).forEach( word => blacklisted[ word ] = true );
+
+export default function makeLegalIdentifier ( str ) {
+ str = str
+ .replace( /-(\w)/g, ( _, letter ) => letter.toUpperCase() )
+ .replace( /[^$_a-zA-Z0-9]/g, '_' );
+
+ if ( /\d/.test( str[0] ) || blacklisted[ str ] ) str = `_${str}`;
+
+ return str;
+}
diff --git a/src/utils/ensureArray.js b/src/utils/ensureArray.js
new file mode 100644
index 0000000..2669345
--- /dev/null
+++ b/src/utils/ensureArray.js
@@ -0,0 +1,5 @@
+export default function ensureArray ( thing ) {
+ if ( Array.isArray( thing ) ) return thing;
+ if ( thing == undefined ) return [];
+ return [ thing ];
+}
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..a87a2b9
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,198 @@
+var path = require( 'path' );
+var assert = require( 'assert' );
+var utils = require( '..' );
+
+describe( 'rollup-pluginutils', function () {
+ describe( 'createFilter', function () {
+ var createFilter = utils.createFilter;
+
+ it( 'includes by default', function () {
+ var filter = createFilter();
+ assert.ok( filter( path.resolve( 'x' ) ) );
+ });
+
+ it( 'excludes IDs that are not included, if include.length > 0', function () {
+ var filter = createFilter([ 'y' ]);
+ assert.ok( !filter( path.resolve( 'x' ) ) );
+ assert.ok( filter( path.resolve( 'y' ) ) );
+ });
+
+ it( 'excludes IDs explicitly', function () {
+ var filter = createFilter( null, [ 'y' ]);
+ assert.ok( filter( path.resolve( 'x' ) ) );
+ assert.ok( !filter( path.resolve( 'y' ) ) );
+ });
+
+ it( 'handles non-array arguments', function () {
+ var filter = createFilter( 'foo/*', 'foo/baz' );
+ assert.ok( filter( path.resolve( 'foo/bar' ) ) );
+ assert.ok( !filter( path.resolve( 'foo/baz' ) ) );
+ });
+
+ it( 'negation patterns', function () {
+ var filter = createFilter([ 'a/!(b)/c' ]);
+ assert.ok( filter( path.resolve( 'a/d/c' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b/c' ) ) );
+ });
+
+ it( 'excludes non-string IDs', function () {
+ var filter = createFilter( null, null );
+ assert.ok( !filter({}) );
+ });
+
+ it( 'excludes strings beginning with NUL', function () {
+ var filter = createFilter( null, null );
+ assert.ok( !filter( '\0someid' ) );
+ });
+
+ it( 'includes with regexp', function () {
+ var filter = createFilter(['a/!(b)/c' , /\.js$/ ]);
+ assert.ok( filter( path.resolve( 'a/d/c' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b/c' ) ) );
+ assert.ok( filter( path.resolve( 'a.js' ) ) );
+ assert.ok( filter( path.resolve( 'a/b.js' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b.jsx' ) ) );
+ })
+
+ it ('excludes with regexp', function () {
+ var filter = createFilter(['a/!(b)/c' , /\.js$/ ], /\.js$/);
+ assert.ok( filter( path.resolve( 'a/d/c' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b/c' ) ) );
+ assert.ok( !filter( path.resolve( 'a.js' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b.js' ) ) );
+ assert.ok( !filter( path.resolve( 'a/b.jsx' ) ) );
+ })
+ });
+
+ describe( 'addExtension', function () {
+ var addExtension = utils.addExtension;
+
+ it( 'adds .js to an ID without an extension', function () {
+ assert.equal( addExtension( 'foo' ), 'foo.js' );
+ });
+
+ it( 'ignores file with existing extension', function () {
+ assert.equal( addExtension( 'foo.js' ), 'foo.js' );
+ assert.equal( addExtension( 'foo.json' ), 'foo.json' );
+ });
+
+ it( 'ignores file with trailing dot', function () {
+ assert.equal( addExtension( 'foo.' ), 'foo.' );
+ });
+
+ it( 'ignores leading .', function () {
+ assert.equal( addExtension( './foo' ), './foo.js' );
+ assert.equal( addExtension( './foo.js' ), './foo.js' );
+ });
+
+ it( 'adds a custom extension', function () {
+ assert.equal( addExtension( 'foo', '.wut' ), 'foo.wut' );
+ assert.equal( addExtension( 'foo.lol', '.wut' ), 'foo.lol' );
+ });
+ });
+
+ describe( 'attachScopes', function () {
+ var attachScopes = utils.attachScopes;
+
+ it( 'attaches a scope to the top level', function () {
+ var ast = {
+ "type": "Program",
+ "start": 0,
+ "end": 8,
+ "body": [
+ {
+ "type": "VariableDeclaration",
+ "start": 0,
+ "end": 8,
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 4,
+ "end": 7,
+ "id": {
+ "type": "Identifier",
+ "start": 4,
+ "end": 7,
+ "name": "foo"
+ },
+ "init": null
+ }
+ ],
+ "kind": "var"
+ }
+ ],
+ "sourceType": "module"
+ };
+
+ var scope = attachScopes( ast, 'scope' );
+ assert.ok( scope.contains( 'foo' ) );
+ assert.ok( !scope.contains( 'bar' ) );
+ });
+
+ it( 'adds multiple declarators from a single var declaration', function () {
+ var ast = {
+ "type": "Program",
+ "start": 0,
+ "end": 13,
+ "body": [
+ {
+ "type": "VariableDeclaration",
+ "start": 0,
+ "end": 13,
+ "declarations": [
+ {
+ "type": "VariableDeclarator",
+ "start": 4,
+ "end": 7,
+ "id": {
+ "type": "Identifier",
+ "start": 4,
+ "end": 7,
+ "name": "foo"
+ },
+ "init": null
+ },
+
+ {
+ "type": "VariableDeclarator",
+ "start": 9,
+ "end": 12,
+ "id": {
+ "type": "Identifier",
+ "start": 9,
+ "end": 12,
+ "name": "bar"
+ },
+ "init": null
+ }
+ ],
+ "kind": "var"
+ }
+ ],
+ "sourceType": "module"
+ };
+
+ var scope = attachScopes( ast, 'scope' );
+ assert.ok( scope.contains( 'foo' ) );
+ assert.ok( scope.contains( 'bar' ) );
+ });
+
+ // TODO more tests
+ });
+
+ describe( 'makeLegalIdentifier', function () {
+ var makeLegalIdentifier = utils.makeLegalIdentifier;
+
+ it( 'camel-cases names', function () {
+ assert.equal( makeLegalIdentifier( 'foo-bar' ), 'fooBar' );
+ });
+
+ it( 'replaces keywords', function () {
+ assert.equal( makeLegalIdentifier( 'typeof' ), '_typeof' );
+ });
+
+ it( 'blacklists arguments (https://github.com/rollup/rollup/issues/871)', function () {
+ assert.equal( makeLegalIdentifier( 'arguments' ), '_arguments' );
+ });
+ });
+});
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-rollup-pluginutils.git
More information about the Pkg-javascript-commits
mailing list