<commit_before>/*

grammar.js - grammar for ASCII text representation of PEG

The MIT License (MIT)

Copyright (c) 2015 Dale Schumacher

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

*/
"use strict";

var grammar = module.exports;

var PEG = require('./index.js');
var named = require('./named.js');

/*
 * build(sponsor[, log]) - construct parsing actors for PEG grammar
 */
grammar.build = function build(sponsor, log) {
    var ns = named.scope(sponsor, { log: log });

    /*
    Grammar <- _ Rule+ EOF
    */
    ns.define('Grammar',
        sponsor(PEG.sequence([
            ns.lookup('_'),
            sponsor(PEG.oneOrMore(
                ns.lookup('Rule')
            )),
            ns.lookup('EOF')
        ]))
    );
    /*
    Rule <- Name LEFTARROW Expression
    */
    ns.define('Rule',
        sponsor(PEG.sequence([
            ns.lookup('Name'),
            ns.lookup('LEFTARROW'),
            ns.lookup('Expression')
        ]))
    );
    /*
    Expression <- Sequence (SLASH Sequence)*
    */
    ns.define('Expression',
        sponsor(PEG.sequence([
            ns.lookup('Sequence'),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.sequence([
                    ns.lookup('SLASH'),
                    ns.lookup('Sequence')
                ]))
            ))
        ]))
    );
    /*
    Sequence <- Prefix*
    */
    ns.define('Sequence',
        sponsor(PEG.zeroOrMore(
            ns.lookup('Prefix')
        ))
    );
    /*
    Prefix <- (AND / NOT)? Suffix
    */
    ns.define('Prefix',
        sponsor(PEG.sequence([
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.choice([
                    ns.lookup('AND'),
                    ns.lookup('NOT')
                ]))
            )),
            ns.lookup('Suffix')
        ]))
    );
    /*
    Suffix <- Primary (QUESTION / STAR / PLUS)?
    */
    ns.define('Suffix',
        sponsor(PEG.sequence([
            ns.lookup('Primary'),
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.choice([
                    ns.lookup('QUESTION'),
                    ns.lookup('STAR'),
                    ns.lookup('PLUS')
                ]))
            ))
        ]))
    );
    /*
    Primary <- Name !LEFTARROW
             / OPEN Expression CLOSE
             / Literal
             / Class
             / Object
             / DOT
    */
    ns.define('Primary',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                ns.lookup('Name'),
                sponsor(PEG.not(
                    ns.lookup('LEFTARROW')
                ))
            ])),
            sponsor(PEG.sequence([
                ns.lookup('OPEN'),
                ns.lookup('Expression'),
                ns.lookup('CLOSE')
            ])),
            ns.lookup('Literal'),
            ns.lookup('Class'),
            ns.lookup('Object'),
            ns.lookup('DOT')
        ]))
    );

    /*
    Name <- [a-zA-Z_] [a-zA-Z_0-9]* _
    */
    ns.define('Name',
        sponsor(PEG.sequence([
            sponsor(PEG.predicate(function(token) {
                return /[a-zA-Z_]/.test(token);
            })),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.predicate(function(token) {
                    return /[a-zA-Z_0-9]/.test(token);
                }))
            )),
            ns.lookup('_')
        ]))
    );
    /*
    Literal <- ["] (!["] Character)+ ["] _
             / ['] (!['] Character)+ ['] _
    */
    ns.define('Literal',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('"')),
                sponsor(PEG.oneOrMore(
                    sponsor(PEG.sequence([
                        sponsor(PEG.not(
                            sponsor(PEG.terminal('"'))
                        )),
                        ns.lookup('Character')
                    ]))
                )),
                sponsor(PEG.terminal('"')),
                ns.lookup('_')
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal("'")),
                sponsor(PEG.oneOrMore(
                    sponsor(PEG.sequence([
                        sponsor(PEG.not(
                            sponsor(PEG.terminal("'"))
                        )),
                        ns.lookup('Character')
                    ]))
                )),
                sponsor(PEG.terminal("'")),
                ns.lookup('_')
            ]))
        ]))
    );
    /*
    Class <- "[" (!"]" Range)+ "]" _
    */
    ns.define('Class',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('[')),
            sponsor(PEG.oneOrMore(
                sponsor(PEG.sequence([
                    sponsor(PEG.not(
                        sponsor(PEG.terminal(']'))
                    )),
                    ns.lookup('Range')
                ]))
            )),
            sponsor(PEG.terminal(']')),
            ns.lookup('_')
        ]))
    );
    /*
    Range <- Character "-" Character / Character
    */
    ns.define('Range',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                ns.lookup('Character'),
                sponsor(PEG.terminal('-')),
                ns.lookup('Character')
            ])),
            ns.lookup('Character')
        ]))
    );
    /*
    Character <- "\\" [nrt'"\[\]\\]
               / "\\u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]
               / !"\\" .
    */
    ns.define('Character',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\\')),
                sponsor(PEG.predicate(function(token) {
                    return /[nrt'"\[\]\\]/.test(token);
                }))
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\\')),
                sponsor(PEG.terminal('u')),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                }))
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.not(
                    sponsor(PEG.terminal('\\'))
                )),
                sponsor(PEG.anything)
            ]))
        ]))
    );
    /*
    Object <- LBRACE (Property (COMMA Property)*)? RBRACE
    */
    ns.define('Object',
        sponsor(PEG.sequence([
            ns.lookup('LBRACE'),
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.sequence([
                    ns.lookup('Property'),
                    sponsor(PEG.zeroOrMore(
                        sponsor(PEG.sequence([
                            ns.lookup('COMMA'),
                            ns.lookup('Property')
                        ]))
                    ))
                ]))
            )),
            ns.lookup('RBRACE')
        ]))
    );
    /*
    Property <- Name COLON Expression
    */
    ns.define('Property',
        sponsor(PEG.sequence([
            ns.lookup('Name'),
            ns.lookup('COLON'),
            ns.lookup('Literal')
        ]))
    );

    /*
    DOT <- "." _
    */
    ns.define('DOT',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('.')),
            ns.lookup('_')
        ]))
    );

    /*
    LEFTARROW <- "<-" _
    */
    ns.define('LEFTARROW',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('<')),
            sponsor(PEG.terminal('-')),
            ns.lookup('_')
        ]))
    );
    /*
    SLASH <- "/" _
    */
    ns.define('SLASH',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('/')),
            ns.lookup('_')
        ]))
    );
    /*
    AND <- "&" _
    */
    ns.define('AND',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('&')),
            ns.lookup('_')
        ]))
    );
    /*
    NOT <- "!" _
    */
    ns.define('NOT',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('!')),
            ns.lookup('_')
        ]))
    );
    /*
    QUESTION <- "?" _
    */
    ns.define('QUESTION',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('?')),
            ns.lookup('_')
        ]))
    );
    /*
    STAR <- "*" _
    */
    ns.define('STAR',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('*')),
            ns.lookup('_')
        ]))
    );
    /*
    PLUS <- "+" _
    */
    ns.define('PLUS',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('+')),
            ns.lookup('_')
        ]))
    );
    /*
    OPEN <- "(" _
    */
    ns.define('OPEN',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('(')),
            ns.lookup('_')
        ]))
    );
    /*
    CLOSE <- ")" _
    */
    ns.define('CLOSE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(')')),
            ns.lookup('_')
        ]))
    );
    /*
    LBRACE <- "{" _
    */
    ns.define('LBRACE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('{')),
            ns.lookup('_')
        ]))
    );
    /*
    RBRACE <- "}" _
    */
    ns.define('RBRACE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('}')),
            ns.lookup('_')
        ]))
    );
    /*
    COLON <- ":" _
    */
    ns.define('COLON',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(':')),
            ns.lookup('_')
        ]))
    );
    /*
    COMMA <- "," _
    */
    ns.define('COMMA',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(',')),
            ns.lookup('_')
        ]))
    );

    /*
    _ <- (Space / Comment)*  # optional whitespace
    */
    ns.define('_',  // optional whitespace
        sponsor(PEG.zeroOrMore(
            sponsor(PEG.choice([
                ns.lookup('Space'),
                ns.lookup('Comment')
            ]))
        ))
    );
    /*
    Comment <- "#" (!EOL .)*
    */
    ns.define('Comment',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('#')),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.sequence([
                    sponsor(PEG.not(
                        ns.lookup('EOL')
                    )),
                    sponsor(PEG.anything)
                ]))
            ))
        ]))
    );
    /*
    Space <- [ \t-\r]
    */
    ns.define('Space',
        sponsor(PEG.predicate(function(token) {
            return /\s/.test(token);
        }))
    );
    /*
    EOL <- "\n"
         / "\r" "\n"?
    */
    ns.define('EOL',
        sponsor(PEG.choice([
            sponsor(PEG.terminal('\n')),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\r')),
                sponsor(PEG.zeroOrOne(
                    sponsor(PEG.terminal('\n'))
                ))
            ]))
        ]))
    );
    /*
    EOF <- !.
    */
    ns.define('EOF',
        sponsor(PEG.not(
            sponsor(PEG.anything)
        ))
    );

    return ns;  // return grammar namespace
};
<commit_msg>Property grammar rule should match the code<commit_after>/*

grammar.js - grammar for ASCII text representation of PEG

The MIT License (MIT)

Copyright (c) 2015 Dale Schumacher

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

*/
"use strict";

var grammar = module.exports;

var PEG = require('./index.js');
var named = require('./named.js');

/*
 * build(sponsor[, log]) - construct parsing actors for PEG grammar
 */
grammar.build = function build(sponsor, log) {
    var ns = named.scope(sponsor, { log: log });

    /*
    Grammar <- _ Rule+ EOF
    */
    ns.define('Grammar',
        sponsor(PEG.sequence([
            ns.lookup('_'),
            sponsor(PEG.oneOrMore(
                ns.lookup('Rule')
            )),
            ns.lookup('EOF')
        ]))
    );
    /*
    Rule <- Name LEFTARROW Expression
    */
    ns.define('Rule',
        sponsor(PEG.sequence([
            ns.lookup('Name'),
            ns.lookup('LEFTARROW'),
            ns.lookup('Expression')
        ]))
    );
    /*
    Expression <- Sequence (SLASH Sequence)*
    */
    ns.define('Expression',
        sponsor(PEG.sequence([
            ns.lookup('Sequence'),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.sequence([
                    ns.lookup('SLASH'),
                    ns.lookup('Sequence')
                ]))
            ))
        ]))
    );
    /*
    Sequence <- Prefix*
    */
    ns.define('Sequence',
        sponsor(PEG.zeroOrMore(
            ns.lookup('Prefix')
        ))
    );
    /*
    Prefix <- (AND / NOT)? Suffix
    */
    ns.define('Prefix',
        sponsor(PEG.sequence([
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.choice([
                    ns.lookup('AND'),
                    ns.lookup('NOT')
                ]))
            )),
            ns.lookup('Suffix')
        ]))
    );
    /*
    Suffix <- Primary (QUESTION / STAR / PLUS)?
    */
    ns.define('Suffix',
        sponsor(PEG.sequence([
            ns.lookup('Primary'),
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.choice([
                    ns.lookup('QUESTION'),
                    ns.lookup('STAR'),
                    ns.lookup('PLUS')
                ]))
            ))
        ]))
    );
    /*
    Primary <- Name !LEFTARROW
             / OPEN Expression CLOSE
             / Literal
             / Class
             / Object
             / DOT
    */
    ns.define('Primary',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                ns.lookup('Name'),
                sponsor(PEG.not(
                    ns.lookup('LEFTARROW')
                ))
            ])),
            sponsor(PEG.sequence([
                ns.lookup('OPEN'),
                ns.lookup('Expression'),
                ns.lookup('CLOSE')
            ])),
            ns.lookup('Literal'),
            ns.lookup('Class'),
            ns.lookup('Object'),
            ns.lookup('DOT')
        ]))
    );

    /*
    Name <- [a-zA-Z_] [a-zA-Z_0-9]* _
    */
    ns.define('Name',
        sponsor(PEG.sequence([
            sponsor(PEG.predicate(function(token) {
                return /[a-zA-Z_]/.test(token);
            })),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.predicate(function(token) {
                    return /[a-zA-Z_0-9]/.test(token);
                }))
            )),
            ns.lookup('_')
        ]))
    );
    /*
    Literal <- ["] (!["] Character)+ ["] _
             / ['] (!['] Character)+ ['] _
    */
    ns.define('Literal',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('"')),
                sponsor(PEG.oneOrMore(
                    sponsor(PEG.sequence([
                        sponsor(PEG.not(
                            sponsor(PEG.terminal('"'))
                        )),
                        ns.lookup('Character')
                    ]))
                )),
                sponsor(PEG.terminal('"')),
                ns.lookup('_')
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal("'")),
                sponsor(PEG.oneOrMore(
                    sponsor(PEG.sequence([
                        sponsor(PEG.not(
                            sponsor(PEG.terminal("'"))
                        )),
                        ns.lookup('Character')
                    ]))
                )),
                sponsor(PEG.terminal("'")),
                ns.lookup('_')
            ]))
        ]))
    );
    /*
    Class <- "[" (!"]" Range)+ "]" _
    */
    ns.define('Class',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('[')),
            sponsor(PEG.oneOrMore(
                sponsor(PEG.sequence([
                    sponsor(PEG.not(
                        sponsor(PEG.terminal(']'))
                    )),
                    ns.lookup('Range')
                ]))
            )),
            sponsor(PEG.terminal(']')),
            ns.lookup('_')
        ]))
    );
    /*
    Range <- Character "-" Character / Character
    */
    ns.define('Range',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                ns.lookup('Character'),
                sponsor(PEG.terminal('-')),
                ns.lookup('Character')
            ])),
            ns.lookup('Character')
        ]))
    );
    /*
    Character <- "\\" [nrt'"\[\]\\]
               / "\\u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]
               / !"\\" .
    */
    ns.define('Character',
        sponsor(PEG.choice([
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\\')),
                sponsor(PEG.predicate(function(token) {
                    return /[nrt'"\[\]\\]/.test(token);
                }))
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\\')),
                sponsor(PEG.terminal('u')),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                })),
                sponsor(PEG.predicate(function(token) {
                    return /[0-9a-fA-F]/.test(token);
                }))
            ])),
            sponsor(PEG.sequence([
                sponsor(PEG.not(
                    sponsor(PEG.terminal('\\'))
                )),
                sponsor(PEG.anything)
            ]))
        ]))
    );
    /*
    Object <- LBRACE (Property (COMMA Property)*)? RBRACE
    */
    ns.define('Object',
        sponsor(PEG.sequence([
            ns.lookup('LBRACE'),
            sponsor(PEG.zeroOrOne(
                sponsor(PEG.sequence([
                    ns.lookup('Property'),
                    sponsor(PEG.zeroOrMore(
                        sponsor(PEG.sequence([
                            ns.lookup('COMMA'),
                            ns.lookup('Property')
                        ]))
                    ))
                ]))
            )),
            ns.lookup('RBRACE')
        ]))
    );
    /*
    Property <- Name COLON Literal
    */
    ns.define('Property',
        sponsor(PEG.sequence([
            ns.lookup('Name'),
            ns.lookup('COLON'),
            ns.lookup('Literal')
        ]))
    );

    /*
    DOT <- "." _
    */
    ns.define('DOT',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('.')),
            ns.lookup('_')
        ]))
    );

    /*
    LEFTARROW <- "<-" _
    */
    ns.define('LEFTARROW',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('<')),
            sponsor(PEG.terminal('-')),
            ns.lookup('_')
        ]))
    );
    /*
    SLASH <- "/" _
    */
    ns.define('SLASH',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('/')),
            ns.lookup('_')
        ]))
    );
    /*
    AND <- "&" _
    */
    ns.define('AND',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('&')),
            ns.lookup('_')
        ]))
    );
    /*
    NOT <- "!" _
    */
    ns.define('NOT',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('!')),
            ns.lookup('_')
        ]))
    );
    /*
    QUESTION <- "?" _
    */
    ns.define('QUESTION',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('?')),
            ns.lookup('_')
        ]))
    );
    /*
    STAR <- "*" _
    */
    ns.define('STAR',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('*')),
            ns.lookup('_')
        ]))
    );
    /*
    PLUS <- "+" _
    */
    ns.define('PLUS',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('+')),
            ns.lookup('_')
        ]))
    );
    /*
    OPEN <- "(" _
    */
    ns.define('OPEN',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('(')),
            ns.lookup('_')
        ]))
    );
    /*
    CLOSE <- ")" _
    */
    ns.define('CLOSE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(')')),
            ns.lookup('_')
        ]))
    );
    /*
    LBRACE <- "{" _
    */
    ns.define('LBRACE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('{')),
            ns.lookup('_')
        ]))
    );
    /*
    RBRACE <- "}" _
    */
    ns.define('RBRACE',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('}')),
            ns.lookup('_')
        ]))
    );
    /*
    COLON <- ":" _
    */
    ns.define('COLON',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(':')),
            ns.lookup('_')
        ]))
    );
    /*
    COMMA <- "," _
    */
    ns.define('COMMA',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal(',')),
            ns.lookup('_')
        ]))
    );

    /*
    _ <- (Space / Comment)*  # optional whitespace
    */
    ns.define('_',  // optional whitespace
        sponsor(PEG.zeroOrMore(
            sponsor(PEG.choice([
                ns.lookup('Space'),
                ns.lookup('Comment')
            ]))
        ))
    );
    /*
    Comment <- "#" (!EOL .)*
    */
    ns.define('Comment',
        sponsor(PEG.sequence([
            sponsor(PEG.terminal('#')),
            sponsor(PEG.zeroOrMore(
                sponsor(PEG.sequence([
                    sponsor(PEG.not(
                        ns.lookup('EOL')
                    )),
                    sponsor(PEG.anything)
                ]))
            ))
        ]))
    );
    /*
    Space <- [ \t-\r]
    */
    ns.define('Space',
        sponsor(PEG.predicate(function(token) {
            return /\s/.test(token);
        }))
    );
    /*
    EOL <- "\n"
         / "\r" "\n"?
    */
    ns.define('EOL',
        sponsor(PEG.choice([
            sponsor(PEG.terminal('\n')),
            sponsor(PEG.sequence([
                sponsor(PEG.terminal('\r')),
                sponsor(PEG.zeroOrOne(
                    sponsor(PEG.terminal('\n'))
                ))
            ]))
        ]))
    );
    /*
    EOF <- !.
    */
    ns.define('EOF',
        sponsor(PEG.not(
            sponsor(PEG.anything)
        ))
    );

    return ns;  // return grammar namespace
};
<|endoftext|>


<commit_before>'use strict'

const promisify = require('promisify-es6')

const transform = function (res, callback) {
  callback(null, res.Entries.map((entry) => {
    return {
      name: entry.Name,
      type: entry.Type,
      size: entry.Size,
      hash: entry.Hash
    }
  }))
}

module.exports = (send) => {
  return promisify((args, opts, callback) => {
    if (typeof (opts) === 'function') {
      callback = opts
      opts = {}
    }
    return send.andTransform({
      path: 'files/ls',
      args: args,
      qs: opts
    }, transform, callback)
  })
}
<commit_msg>fix: exception when dir is empty (#680)<commit_after>'use strict'

const promisify = require('promisify-es6')

const transform = function (res, callback) {
  const entries = res.Entries || []

  callback(null, entries.map((entry) => {
    return {
      name: entry.Name,
      type: entry.Type,
      size: entry.Size,
      hash: entry.Hash
    }
  }))
}

module.exports = (send) => {
  return promisify((args, opts, callback) => {
    if (typeof (opts) === 'function') {
      callback = opts
      opts = {}
    }
    return send.andTransform({
      path: 'files/ls',
      args: args,
      qs: opts
    }, transform, callback)
  })
}
<|endoftext|>


<commit_before>/* global createjs */

var flowjs = flowjs || {};

flowjs.flowItem = function flowItem(x, y, text, radius, listener){
    this.x = x;
    this.y = y;

    this.color = "black";
    this.background = "blue";
    this.alpha = 1;
    this.radius = radius || 40;
    this.text = text || "Hello";
    this.link = "#";
    this.font = "Arial";
    this.fontSize = "16px";
    this.strokeWidth = 2;

    this.circle = new createjs.Shape();
    this.textShape = new createjs.Text();

    this.listener = listener || function(){/*override this function*/};

    var that = this;
    var onclick = function(){
        console.log("click");
        window.open(this.link);
    };

    var onmouseover = function(){
        that.originalColor = that.color;
        that.color = "green";
        that.updateShape();
    };

    var onmouseout = function(){
        that.color = that.originalColor || "black";
        that.updateShape();
    };

    this.circle.addEventListener("click",           onclick);
    this.textShape.addEventListener("click",        onclick);
    this.circle.addEventListener("mouseout",        onmouseout);
    this.textShape.addEventListener("mouseout",     onmouseout);
    this.circle.addEventListener("mouseover",       onmouseover);
    this.textShape.addEventListener("mouseover",    onmouseover);

};

flowjs.flowItem.prototype.refresh = function(){
    this.circle.graphics.clear();
    this.circle.graphics.setStrokeStyle(this.strokeWidth).beginStroke(this.color).beginFill(this.background).drawCircle(0, 0, this.radius);
    this.circle.x = this.getLocation().x;
    this.circle.y = this.getLocation().y;
    this.circle.alpha = this.alpha;

    this.textShape.text = this.text;
    this.textShape.color = this.color;
    this.textShape.font = this.fontSize + " " + this.font;

    var textWidth = this.textShape.getBounds().width;
    this.textShape.x = this.x + this.radius - (textWidth/2);
    this.textShape.y = this.y + (this.radius * 2.2);
    this.textShape.alpha = this.alpha;

    this.loadingAnimation = this.loadingAnimation || this._getLoadingAnimation();
};

flowjs.flowItem.prototype.getLocation = function(){
    return {x: this.getX(), y: this.getY()};
};

flowjs.flowItem.prototype.getX = function(){
    return this.x + this.radius;
};

flowjs.flowItem.prototype.getY = function(){
    return this.y + this.radius;
};

flowjs.flowItem.prototype.setX = function(x){
    this.x = x - this.radius;
};

flowjs.flowItem.prototype.setY = function(y){
    this.y = y - this.radius;
};

flowjs.flowItem.prototype.updateShape = function(){
    this.refresh();
    this.listener();
};

flowjs.flowItem.prototype.getDrawableItems = function(){
    return [this.circle, this.textShape];
};

flowjs.flowItem.prototype.toggleFlashing = function(){
    var isPaused = this.loadingAnimation._paused;
    this.loadingAnimation.setPaused(!isPaused);
};

flowjs.flowItem.prototype._getLoadingAnimation = function(){
    var y = this.getLocation().y;
    var distance = this.radius/2;

    var anim = createjs.Tween.get(this.circle, {loop: true, paused: true})
        // .to({ y: y}, 100,           createjs.Ease.getPowIn(2.2))
        .to({ y: y-distance}, 300,  createjs.Ease.getPowIn(2))
        .to({ y: y+distance}, 300,  createjs.Ease.getPowIn(2))
        .to({ y: y}, 100);
        // .wait(100);
    anim.setPaused(true);
    return anim;
};<commit_msg>fixed item link on click<commit_after>/* global createjs */

var flowjs = flowjs || {};

flowjs.flowItem = function flowItem(x, y, text, radius, listener){
    this.x = x;
    this.y = y;

    this.color = "black";
    this.background = "blue";
    this.alpha = 1;
    this.radius = radius || 40;
    this.text = text || "Hello";
    this.link = "#";
    this.font = "Arial";
    this.fontSize = "16px";
    this.strokeWidth = 2;

    this.circle = new createjs.Shape();
    this.textShape = new createjs.Text();

    this.listener = listener || function(){/*override this function*/};

    var that = this;
    var onclick = function(){
        console.log("click");
        window.open(that.link);
    };

    var onmouseover = function(){
        that.originalColor = that.color;
        that.color = "green";
        that.updateShape();
    };

    var onmouseout = function(){
        that.color = that.originalColor || "black";
        that.updateShape();
    };

    this.circle.addEventListener("click",           onclick);
    this.textShape.addEventListener("click",        onclick);
    this.circle.addEventListener("mouseout",        onmouseout);
    this.textShape.addEventListener("mouseout",     onmouseout);
    this.circle.addEventListener("mouseover",       onmouseover);
    this.textShape.addEventListener("mouseover",    onmouseover);

};

flowjs.flowItem.prototype.refresh = function(){
    this.circle.graphics.clear();
    this.circle.graphics.setStrokeStyle(this.strokeWidth).beginStroke(this.color).beginFill(this.background).drawCircle(0, 0, this.radius);
    this.circle.x = this.getLocation().x;
    this.circle.y = this.getLocation().y;
    this.circle.alpha = this.alpha;

    this.textShape.text = this.text;
    this.textShape.color = this.color;
    this.textShape.font = this.fontSize + " " + this.font;

    var textWidth = this.textShape.getBounds().width;
    this.textShape.x = this.x + this.radius - (textWidth/2);
    this.textShape.y = this.y + (this.radius * 2.2);
    this.textShape.alpha = this.alpha;

    this.loadingAnimation = this.loadingAnimation || this._getLoadingAnimation();
};

flowjs.flowItem.prototype.getLocation = function(){
    return {x: this.getX(), y: this.getY()};
};

flowjs.flowItem.prototype.getX = function(){
    return this.x + this.radius;
};

flowjs.flowItem.prototype.getY = function(){
    return this.y + this.radius;
};

flowjs.flowItem.prototype.setX = function(x){
    this.x = x - this.radius;
};

flowjs.flowItem.prototype.setY = function(y){
    this.y = y - this.radius;
};

flowjs.flowItem.prototype.updateShape = function(){
    this.refresh();
    this.listener();
};

flowjs.flowItem.prototype.getDrawableItems = function(){
    return [this.circle, this.textShape];
};

flowjs.flowItem.prototype.toggleFlashing = function(){
    var isPaused = this.loadingAnimation._paused;
    this.loadingAnimation.setPaused(!isPaused);
};

flowjs.flowItem.prototype._getLoadingAnimation = function(){
    var y = this.getLocation().y;
    var distance = this.radius/2;

    var anim = createjs.Tween.get(this.circle, {loop: true, paused: true})
        // .to({ y: y}, 100,           createjs.Ease.getPowIn(2.2))
        .to({ y: y-distance}, 300,  createjs.Ease.getPowIn(2))
        .to({ y: y+distance}, 300,  createjs.Ease.getPowIn(2))
        .to({ y: y}, 100);
        // .wait(100);
    anim.setPaused(true);
    return anim;
};<|endoftext|>


<commit_before>
const typescript = require('rollup-plugin-typescript2');
const glsl = require('rollup-plugin-glsl');
const path = require('path');
const fs = require('fs');
var matched = require('matched');
const production = !process.env.ROLLUP_WATCH;
const gulp = require('gulp');
const rollup = require('rollup');
//处理文件流使用的插件
var through = require('through2');
//合并文件
var concat = require('gulp-concat'),pump = require('pump');
const uglify = require('gulp-uglify-es').default;
const rename = require('gulp-rename');

//编译新的库文件只需要在packsDef中配置一下新的库就可以了
var packsDef = [
    {
        'libName': "core",
        'input': [
            './layaAir/Config.ts',
            './layaAir/laya/Const.ts',
            './layaAir/ILaya.ts',
            './layaAir/Laya.ts',
            './layaAir/laya/components/**/*.*',
            './layaAir/laya/display/**/*.*',
            './layaAir/laya/events/**/*.*',
            './layaAir/laya/filters/**/*.*',
            './layaAir/laya/layagl/**/*.*',
            './layaAir/laya/maths/**/*.*',
            './layaAir/laya/media/**/*.*',
            './layaAir/laya/net/**/*.*',
            './layaAir/laya/renders/**/*.*',
            './layaAir/laya/resource/**/*.*',
            './layaAir/laya/system/**/*.*',
            './layaAir/laya/utils/**/*.*',
            './layaAir/laya/webgl/**/*.*',
            './layaAir/laya/effect/**/*.*'
        ],
        'out': '../build/js/libs/laya.core.js'
    },
    {
        'libName': "d3",
        'input': [
            './layaAir/laya/d3/animation/**/*.*',
            './layaAir/laya/d3/component/**/*.*',
            './layaAir/laya/d3/core/**/*.*',
            './layaAir/laya/d3/graphics/**/*.*',
            './layaAir/laya/d3/loaders/**/*.*',
            './layaAir/laya/d3/math/**/*.*',
            './layaAir/laya/d3/physics/**/*.*',
            './layaAir/laya/d3/shader/**/*.*',
            './layaAir/laya/d3/shadowMap/**/*.*',
            './layaAir/laya/d3/text/**/*.*',
            './layaAir/laya/d3/utils/**/*.*',
            './layaAir/laya/d3/CastShadowList.ts',
            './layaAir/laya/d3/Input3D.ts',
            './layaAir/laya/d3/MouseTouch.ts',
            './layaAir/laya/d3/Touch.ts',
            './layaAir/laya/d3/resource/**/*.*',
            './layaAir/Config3D.ts',
            './layaAir/ILaya3D.ts',
            './layaAir/Laya3D.ts'
        ],
        'out': '../build/js/libs/laya.d3.js'
    },
    {
        'libName': "gltf",
        'input': [
            './layaAir/laya/gltf/**/*.*',
        ],
        'out': '../build/js/libs/laya.gltf.js'
    },
    {
        'libName': "cannonPhysics",
        'input': [
            './layaAir/laya/d3/physicsCannon/**/*.*',
        ],
        'out': '../build/js/libs/laya.cannonPhysics.js'
    },

    {
        'libName': 'device',
        'input': [
            './layaAir/laya/device/**/*.*'
        ],
        'out': '../build/js/libs/laya.device.js'
    },
    {
        'libName': 'tiledmap',
        'input': [
            './layaAir/laya/map/**/*.*'
        ],
        'out': '../build/js/libs/laya.tiledmap.js'
    },
    {
        'libName': 'html',
        'input': [
            './layaAir/laya/html/**/*.*'
        ],
        'out': '../build/js/libs/laya.html.js'
    },
    {
        'libName': 'particle',
        'input': [
            './layaAir/laya/particle/**/*.*'
        ],
        'out': '../build/js/libs/laya.particle.js'
    },

    {
        'libName': 'physics',
        'input': [
            './layaAir/laya/physics/**/*.*'
        ],
        'out': '../build/js/libs/laya.physics.js'
    },
    {
        'libName': 'ui',
        'input': [
            './layaAir/laya/ui/**/*.*',
            './layaAir/UIConfig.ts',
        ],
        'out': '../build/js/libs/laya.ui.js'
    },
    {
        'libName': 'ani',
        'input': [
            './layaAir/laya/ani/**/*.*'
        ],
        'out': '../build/js/libs/laya.ani.js'
    },//debugtool
    {
        'libName': 'debugtool',
        'input': [
            './extensions/debug/**/*.*'
        ],
        'out': '../build/js/libs/laya.debugtool.js'
    }
];

var curPackFiles = null;  //当前包的所有的文件
var mentry = 'multientry:entry-point';
function myMultiInput() {
    var include = [];
    var exclude = [];
    function configure(config) {
        if (typeof config === 'string') {
            include = [config];
        } else if (Array.isArray(config)) {
            include = config;
        } else {
            include = config.include || [];
            exclude = config.exclude || [];

            if (config.exports === false) {
                exporter = function exporter(p) {
                    if (p.substr(p.length - 3) == '.ts') {
                        p = p.substr(0, p.length - 3);
                    }
                    return `import ${JSON.stringify(p)};`;
                };
            }
        }
    }

    var exporter = function exporter(p) {
        if (p.substr(p.length - 3) == '.ts') {
            p = p.substr(0, p.length - 3);
        }
        return `export * from ${JSON.stringify(p)};`;
    };

    return (
        {
            options(options) {
                console.log('===', options.input)
                configure(options.input);
                options.input = mentry;
            },

            resolveId(id, importer) {//entry是个特殊字符串，rollup并不识别，所以假装这里解析一下
                if (id === mentry) {
                    return mentry;
                }
                if (mentry == importer)
                    return;
                var importfile = path.join(path.dirname(importer), id);
                var ext = path.extname(importfile);
                if (ext != '.ts' && ext != '.glsl' && ext != '.vs' && ext != '.ps' && ext != '.fs') {
                    importfile += '.ts';
                }
                //console.log('import ', importfile);
                if (curPackFiles.indexOf(importfile) < 0) {
                    //其他包里的文件
                    // console.log('other pack:',id,'importer=', importer);
                    return 'Laya';
                }
            },
            load(id) {
                if (id === mentry) {
                    if (!include.length) {
                        return Promise.resolve('');
                    }

                    var patterns = include.concat(exclude.map(function (pattern) {
                        return '!' + pattern;
                    }));
                    return matched.promise(patterns, { realpath: true }).then(function (paths) {
                        curPackFiles = paths;   // 记录一下所有的文件
                        paths.sort();
                        return paths.map(exporter).join('\n');
                    });
                } else {
                    //console.log('load ',id);
                }
            }
        }
    );
}

//修改引擎库
gulp.task('ModifierJs', () => {
    for (let i = 0; i < packsDef.length; ++i) {
        if (i !== packsDef.length - 1) {
            gulp.src([packsDef[i].out])
                .pipe(through.obj(function (file, encode, cb) {
                    var srcContents = file.contents.toString();
                    var destContents = srcContents.replace(/var Laya /, "window.Laya");
                    destContents = destContents.replace(/\(this.Laya = this.Laya \|\| {}, Laya\)\);/, "(window.Laya = window.Laya || {}, Laya));");
                    // 再次转为Buffer对象，并赋值给文件内容
                    file.contents = Buffer.from(destContents);
                    // 以下是例行公事
                    this.push(file)
                    cb()
                }))
                .pipe(gulp.dest('../build/js/libs/'));
        }else{
            return gulp.src([packsDef[i].out])
            .pipe(through.obj(function (file, encode, cb) {
                var srcContents = file.contents.toString();
                var destContents = srcContents.replace(/var Laya /, "window.Laya");
                destContents = destContents.replace(/\(this.Laya = this.Laya \|\| {}, Laya\)\);/, "(window.Laya = window.Laya || {}, Laya));");
                // 再次转为Buffer对象，并赋值给文件内容
                file.contents = Buffer.from(destContents);
                // 以下是例行公事
                this.push(file)
                cb()
            }))
            .pipe(gulp.dest('../build/js/libs/'));
        }
    }
});

//合并physics 和 box2d
gulp.task('ConcatBox2dPhysics', function (cb) {
    pump([
        gulp.src([
            './layaAir/jsLibs/box2d.js',
            '../build/js/libs/laya.physics.js']),
        concat('laya.physics.js'),//合并后的文件名
        gulp.dest('../build/js/libs/'),
    ], cb);
});

//拷贝引擎的第三方js库
gulp.task('CopyJSLibsToJS', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','./layaAir/jsLibs/*.js', '!./layaAir/jsLibs/box2d.js', '!./layaAir/jsLibs/laya.physics.js'])
        .pipe(gulp.dest('../build/js/libs'));
});

//拷贝js库至ts库
gulp.task('CopyJSFileToTSCompatible', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','../build/js/libs/**/*.js'])
        .pipe(gulp.dest('../build/ts/libs'));
});

//拷贝js库至as库
gulp.task('CopyJSFileToAS', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','../build/js/libs/**/*.js', '!../build/js/declare/*ts'])
        .pipe(gulp.dest('../build/as/jslibs'));
});

//拷贝引擎ts源码至ts库
gulp.task('CopyTSFileToTS', () => {
    return gulp.src([
        './layaAir/**/*.*', '!./layaAir/jsLibs/**/*.*', '!./layaAir/gulpfile.js', '!./layaAir/tsconfig.json'])
        .pipe(gulp.dest('../build/ts_new/libs'));
});

//拷贝第三方库至ts库(未来在数组中补充需要的其他第三方库)
gulp.task('CopyTSJSLibsFileToTS', () => {
    return gulp.src([
        './layaAir/jsLibs/**/*.*','../build/js/libs/laya.cannonPhysics.js'])
        .pipe(gulp.dest('../build/ts_new/jslibs'));
});

gulp.task('CopyDTS', (cb) => {
    gulp.src(['../tslibs/ts/*.*','./layaAir/glsl.d.ts'])
        .pipe(gulp.dest('../build/js/ts'))
        .pipe(gulp.dest('../build/ts/ts'))

    gulp.src(['../tslibs/nts/*.*'])
        .pipe(gulp.dest('../build/ts_new/libs'))
    setTimeout(cb, 1000);
});

//在这个任务中由于机器的配置可能会出现堆栈溢出的情况，解决方案一可以将其中的某些库移送至buildJS2编译，若buildJS2也堆栈溢出，则可以再新建一个任务buildJS3
gulp.task('buildJS', async function () {
    for (let i = 0; i < packsDef.length; ++i) {
        const subTask = await rollup.rollup({
            input: packsDef[i].input,
            output: {
                extend: true,
                globals: { 'Laya': 'Laya' }
            },
            external: ['Laya'],
            plugins: [
                myMultiInput(),
                typescript({
                    tsconfig: "./layaAir/tsconfig.json",
                    check: false,
                    tsconfigOverride: { compilerOptions: { removeComments: true } }
                }),
                glsl({
                    include: /.*(.glsl|.vs|.fs)$/,
                    sourceMap: false,
                    compress: false
                }),
            ]
        });

        if (packsDef[i].libName == "core") {
            await subTask.write({
                file: packsDef[i].out,
                format: 'iife',
                outro: 'exports.static=_static;',  //由于static是关键字，无法通过ts编译。AS需要这个函数，临时强插
                name: 'Laya',
                sourcemap: false
            });
        }
        else {
            await subTask.write({
                file: packsDef[i].out,
                format: 'iife',
                name: 'Laya',
                sourcemap: false,
                extend: true,
                globals: { 'Laya': 'Laya' }
            });

        }
    }
});

// 压缩
// 下面两个方法，最好能合并
gulp.task("compressJs", function () {
    gulp.src("../build/as/jslibs/laya.physics3D.js")
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));

    gulp.src("../build/as/jslibs/laya.physics3D.wasm.wasm")
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));

    return gulp.src(["../build/as/jslibs/*.js", "!../build/as/jslibs/{laya.physics3D.js}"])
        .pipe(uglify({
            mangle: {
                keep_fnames: true
            }
        }))
        .on('error', function (err) {
            console.warn(err.toString());
        })
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));
});

gulp.task("compresstsnewJs", function () {
    gulp.src("../build/ts_new/jslibs/{laya.physics3D.js,box2d.js}")
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));

    gulp.src("../build/ts_new/jslibs/laya.physics3D.wasm.wasm")
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));

    return gulp.src(["../build/ts_new/jslibs/*.js", "!../build/ts_new/jslibs/{laya.physics3D.js,box2d.js}"])
        .pipe(uglify({
            mangle: {
                keep_fnames: true
            }
        }))
        .on('error', function (err) {
            console.warn(err.toString());
        })
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));
});

gulp.task('build', gulp.series('buildJS', 'ModifierJs', 'ConcatBox2dPhysics', 'CopyJSLibsToJS', 'CopyTSFileToTS', 'CopyJSFileToAS', 'CopyTSJSLibsFileToTS', 'CopyJSFileToTSCompatible', 'CopyDTS', 'compressJs', 'compresstsnewJs'));
<commit_msg>去掉测试的编译<commit_after>
const typescript = require('rollup-plugin-typescript2');
const glsl = require('rollup-plugin-glsl');
const path = require('path');
const fs = require('fs');
var matched = require('matched');
const production = !process.env.ROLLUP_WATCH;
const gulp = require('gulp');
const rollup = require('rollup');
//处理文件流使用的插件
var through = require('through2');
//合并文件
var concat = require('gulp-concat'),pump = require('pump');
const uglify = require('gulp-uglify-es').default;
const rename = require('gulp-rename');

//编译新的库文件只需要在packsDef中配置一下新的库就可以了
var packsDef = [
    {
        'libName': "core",
        'input': [
            './layaAir/Config.ts',
            './layaAir/laya/Const.ts',
            './layaAir/ILaya.ts',
            './layaAir/Laya.ts',
            './layaAir/laya/components/**/*.*',
            './layaAir/laya/display/**/*.*',
            './layaAir/laya/events/**/*.*',
            './layaAir/laya/filters/**/*.*',
            './layaAir/laya/layagl/**/*.*',
            './layaAir/laya/maths/**/*.*',
            './layaAir/laya/media/**/*.*',
            './layaAir/laya/net/**/*.*',
            './layaAir/laya/renders/**/*.*',
            './layaAir/laya/resource/**/*.*',
            './layaAir/laya/system/**/*.*',
            './layaAir/laya/utils/**/*.*',
            './layaAir/laya/webgl/**/*.*',
            './layaAir/laya/effect/**/*.*'
        ],
        'out': '../build/js/libs/laya.core.js'
    },
    {
        'libName': "d3",
        'input': [
            './layaAir/laya/d3/animation/**/*.*',
            './layaAir/laya/d3/component/**/*.*',
            './layaAir/laya/d3/core/**/*.*',
            './layaAir/laya/d3/graphics/**/*.*',
            './layaAir/laya/d3/loaders/**/*.*',
            './layaAir/laya/d3/math/**/*.*',
            './layaAir/laya/d3/physics/**/*.*',
            './layaAir/laya/d3/shader/**/*.*',
            './layaAir/laya/d3/shadowMap/**/*.*',
            './layaAir/laya/d3/text/**/*.*',
            './layaAir/laya/d3/utils/**/*.*',
            './layaAir/laya/d3/CastShadowList.ts',
            './layaAir/laya/d3/Input3D.ts',
            './layaAir/laya/d3/MouseTouch.ts',
            './layaAir/laya/d3/Touch.ts',
            './layaAir/laya/d3/resource/**/*.*',
            './layaAir/Config3D.ts',
            './layaAir/ILaya3D.ts',
            './layaAir/Laya3D.ts'
        ],
        'out': '../build/js/libs/laya.d3.js'
    },
    {
        'libName': "cannonPhysics",
        'input': [
            './layaAir/laya/d3/physicsCannon/**/*.*',
        ],
        'out': '../build/js/libs/laya.cannonPhysics.js'
    },

    {
        'libName': 'device',
        'input': [
            './layaAir/laya/device/**/*.*'
        ],
        'out': '../build/js/libs/laya.device.js'
    },
    {
        'libName': 'tiledmap',
        'input': [
            './layaAir/laya/map/**/*.*'
        ],
        'out': '../build/js/libs/laya.tiledmap.js'
    },
    {
        'libName': 'html',
        'input': [
            './layaAir/laya/html/**/*.*'
        ],
        'out': '../build/js/libs/laya.html.js'
    },
    {
        'libName': 'particle',
        'input': [
            './layaAir/laya/particle/**/*.*'
        ],
        'out': '../build/js/libs/laya.particle.js'
    },

    {
        'libName': 'physics',
        'input': [
            './layaAir/laya/physics/**/*.*'
        ],
        'out': '../build/js/libs/laya.physics.js'
    },
    {
        'libName': 'ui',
        'input': [
            './layaAir/laya/ui/**/*.*',
            './layaAir/UIConfig.ts',
        ],
        'out': '../build/js/libs/laya.ui.js'
    },
    {
        'libName': 'ani',
        'input': [
            './layaAir/laya/ani/**/*.*'
        ],
        'out': '../build/js/libs/laya.ani.js'
    },//debugtool
    {
        'libName': 'debugtool',
        'input': [
            './extensions/debug/**/*.*'
        ],
        'out': '../build/js/libs/laya.debugtool.js'
    }
];

var curPackFiles = null;  //当前包的所有的文件
var mentry = 'multientry:entry-point';
function myMultiInput() {
    var include = [];
    var exclude = [];
    function configure(config) {
        if (typeof config === 'string') {
            include = [config];
        } else if (Array.isArray(config)) {
            include = config;
        } else {
            include = config.include || [];
            exclude = config.exclude || [];

            if (config.exports === false) {
                exporter = function exporter(p) {
                    if (p.substr(p.length - 3) == '.ts') {
                        p = p.substr(0, p.length - 3);
                    }
                    return `import ${JSON.stringify(p)};`;
                };
            }
        }
    }

    var exporter = function exporter(p) {
        if (p.substr(p.length - 3) == '.ts') {
            p = p.substr(0, p.length - 3);
        }
        return `export * from ${JSON.stringify(p)};`;
    };

    return (
        {
            options(options) {
                console.log('===', options.input)
                configure(options.input);
                options.input = mentry;
            },

            resolveId(id, importer) {//entry是个特殊字符串，rollup并不识别，所以假装这里解析一下
                if (id === mentry) {
                    return mentry;
                }
                if (mentry == importer)
                    return;
                var importfile = path.join(path.dirname(importer), id);
                var ext = path.extname(importfile);
                if (ext != '.ts' && ext != '.glsl' && ext != '.vs' && ext != '.ps' && ext != '.fs') {
                    importfile += '.ts';
                }
                //console.log('import ', importfile);
                if (curPackFiles.indexOf(importfile) < 0) {
                    //其他包里的文件
                    // console.log('other pack:',id,'importer=', importer);
                    return 'Laya';
                }
            },
            load(id) {
                if (id === mentry) {
                    if (!include.length) {
                        return Promise.resolve('');
                    }

                    var patterns = include.concat(exclude.map(function (pattern) {
                        return '!' + pattern;
                    }));
                    return matched.promise(patterns, { realpath: true }).then(function (paths) {
                        curPackFiles = paths;   // 记录一下所有的文件
                        paths.sort();
                        return paths.map(exporter).join('\n');
                    });
                } else {
                    //console.log('load ',id);
                }
            }
        }
    );
}

//修改引擎库
gulp.task('ModifierJs', () => {
    for (let i = 0; i < packsDef.length; ++i) {
        if (i !== packsDef.length - 1) {
            gulp.src([packsDef[i].out])
                .pipe(through.obj(function (file, encode, cb) {
                    var srcContents = file.contents.toString();
                    var destContents = srcContents.replace(/var Laya /, "window.Laya");
                    destContents = destContents.replace(/\(this.Laya = this.Laya \|\| {}, Laya\)\);/, "(window.Laya = window.Laya || {}, Laya));");
                    // 再次转为Buffer对象，并赋值给文件内容
                    file.contents = Buffer.from(destContents);
                    // 以下是例行公事
                    this.push(file)
                    cb()
                }))
                .pipe(gulp.dest('../build/js/libs/'));
        }else{
            return gulp.src([packsDef[i].out])
            .pipe(through.obj(function (file, encode, cb) {
                var srcContents = file.contents.toString();
                var destContents = srcContents.replace(/var Laya /, "window.Laya");
                destContents = destContents.replace(/\(this.Laya = this.Laya \|\| {}, Laya\)\);/, "(window.Laya = window.Laya || {}, Laya));");
                // 再次转为Buffer对象，并赋值给文件内容
                file.contents = Buffer.from(destContents);
                // 以下是例行公事
                this.push(file)
                cb()
            }))
            .pipe(gulp.dest('../build/js/libs/'));
        }
    }
});

//合并physics 和 box2d
gulp.task('ConcatBox2dPhysics', function (cb) {
    pump([
        gulp.src([
            './layaAir/jsLibs/box2d.js',
            '../build/js/libs/laya.physics.js']),
        concat('laya.physics.js'),//合并后的文件名
        gulp.dest('../build/js/libs/'),
    ], cb);
});

//拷贝引擎的第三方js库
gulp.task('CopyJSLibsToJS', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','./layaAir/jsLibs/*.js', '!./layaAir/jsLibs/box2d.js', '!./layaAir/jsLibs/laya.physics.js'])
        .pipe(gulp.dest('../build/js/libs'));
});

//拷贝js库至ts库
gulp.task('CopyJSFileToTSCompatible', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','../build/js/libs/**/*.js'])
        .pipe(gulp.dest('../build/ts/libs'));
});

//拷贝js库至as库
gulp.task('CopyJSFileToAS', () => {
    return gulp.src([
        './layaAir/jsLibs/laya.physics3D.wasm.wasm','../build/js/libs/**/*.js', '!../build/js/declare/*ts'])
        .pipe(gulp.dest('../build/as/jslibs'));
});

//拷贝引擎ts源码至ts库
gulp.task('CopyTSFileToTS', () => {
    return gulp.src([
        './layaAir/**/*.*', '!./layaAir/jsLibs/**/*.*', '!./layaAir/gulpfile.js', '!./layaAir/tsconfig.json'])
        .pipe(gulp.dest('../build/ts_new/libs'));
});

//拷贝第三方库至ts库(未来在数组中补充需要的其他第三方库)
gulp.task('CopyTSJSLibsFileToTS', () => {
    return gulp.src([
        './layaAir/jsLibs/**/*.*','../build/js/libs/laya.cannonPhysics.js'])
        .pipe(gulp.dest('../build/ts_new/jslibs'));
});

gulp.task('CopyDTS', (cb) => {
    gulp.src(['../tslibs/ts/*.*','./layaAir/glsl.d.ts'])
        .pipe(gulp.dest('../build/js/ts'))
        .pipe(gulp.dest('../build/ts/ts'))

    gulp.src(['../tslibs/nts/*.*'])
        .pipe(gulp.dest('../build/ts_new/libs'))
    setTimeout(cb, 1000);
});

//在这个任务中由于机器的配置可能会出现堆栈溢出的情况，解决方案一可以将其中的某些库移送至buildJS2编译，若buildJS2也堆栈溢出，则可以再新建一个任务buildJS3
gulp.task('buildJS', async function () {
    for (let i = 0; i < packsDef.length; ++i) {
        const subTask = await rollup.rollup({
            input: packsDef[i].input,
            output: {
                extend: true,
                globals: { 'Laya': 'Laya' }
            },
            external: ['Laya'],
            plugins: [
                myMultiInput(),
                typescript({
                    tsconfig: "./layaAir/tsconfig.json",
                    check: false,
                    tsconfigOverride: { compilerOptions: { removeComments: true } }
                }),
                glsl({
                    include: /.*(.glsl|.vs|.fs)$/,
                    sourceMap: false,
                    compress: false
                }),
            ]
        });

        if (packsDef[i].libName == "core") {
            await subTask.write({
                file: packsDef[i].out,
                format: 'iife',
                outro: 'exports.static=_static;',  //由于static是关键字，无法通过ts编译。AS需要这个函数，临时强插
                name: 'Laya',
                sourcemap: false
            });
        }
        else {
            await subTask.write({
                file: packsDef[i].out,
                format: 'iife',
                name: 'Laya',
                sourcemap: false,
                extend: true,
                globals: { 'Laya': 'Laya' }
            });

        }
    }
});

// 压缩
// 下面两个方法，最好能合并
gulp.task("compressJs", function () {
    gulp.src("../build/as/jslibs/laya.physics3D.js")
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));

    gulp.src("../build/as/jslibs/laya.physics3D.wasm.wasm")
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));

    return gulp.src(["../build/as/jslibs/*.js", "!../build/as/jslibs/{laya.physics3D.js}"])
        .pipe(uglify({
            mangle: {
                keep_fnames: true
            }
        }))
        .on('error', function (err) {
            console.warn(err.toString());
        })
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/as/jslibs/min"))
        .pipe(gulp.dest("../build/js/libs/min"))
        .pipe(gulp.dest("../build/ts/libs/min"));
});

gulp.task("compresstsnewJs", function () {
    gulp.src("../build/ts_new/jslibs/{laya.physics3D.js,box2d.js}")
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));

    gulp.src("../build/ts_new/jslibs/laya.physics3D.wasm.wasm")
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));

    return gulp.src(["../build/ts_new/jslibs/*.js", "!../build/ts_new/jslibs/{laya.physics3D.js,box2d.js}"])
        .pipe(uglify({
            mangle: {
                keep_fnames: true
            }
        }))
        .on('error', function (err) {
            console.warn(err.toString());
        })
        .pipe(rename({extname: ".min.js"}))
        .pipe(gulp.dest("../build/ts_new/jslibs/min"));
});

gulp.task('build', gulp.series('buildJS', 'ModifierJs', 'ConcatBox2dPhysics', 'CopyJSLibsToJS', 'CopyTSFileToTS', 'CopyJSFileToAS', 'CopyTSJSLibsFileToTS', 'CopyJSFileToTSCompatible', 'CopyDTS', 'compressJs', 'compresstsnewJs'));
<|endoftext|>


<commit_before>let Debug = require('debug');
let taskcluster = require('taskcluster-client');
let slugid = require('slugid');
let yaml = require('js-yaml');
let assert = require('assert');
let EventEmitter = require('events');
let _ = require('lodash');
let Promise = require('bluebird');
let prAllowed = require('./pr-allowed');

let INSPECTOR_URL = 'https://tools.taskcluster.net/task-group-inspector/#/';

let debugPrefix = 'taskcluster-github:handlers';
let debug = Debug(debugPrefix);

/**
 * Create handlers
 *
 * options:
 * {
 *   credentials:        // Pulse credentials
 *   queueName:          // Queue name (optional)
 *   reference:          // A taskcluster exchange reference for this service
 *   monitor:            // base.monitor({...})
 *   jobQueueName:       // An optional queue name for the jobs handler
 *   statusQueueName:    // An optional queue name for the status handler
 *   context:            // Things we want to make available inside handlers
 * }
 */
class Handlers {
  constructor({credentials, monitor, reference, jobQueueName, statusQueueName, intree, context}) {
    debug('Constructing handlers...');
    assert(monitor, 'monitor is required for statistics');
    assert(monitor, 'reference must be provided');
    assert(intree, 'intree configuration builder must be provided');
    this.credentials = credentials;
    this.monitor = monitor;
    this.reference = reference;
    this.intree = intree;
    this.connection = null;
    this.statusListener = null;
    this.jobListener = null;
    this.statusQueueName = statusQueueName;  // Optional
    this.jobQueueName = jobQueueName;  // Optional
    this.context = context;

    this.handlerComplete = null;
    this.handlerRejected = null;
  }

  /**
   * Set up the handlers.
   */
  async setup(options) {
    debug('Setting up handlers...');
    options = options || {};
    assert(!this.connection, 'Cannot setup twice!');
    this.connection = new taskcluster.PulseConnection(this.credentials);
    this.statusListener = new taskcluster.PulseListener({
      queueName: this.statusQueueName,
      connection: this.connection,
    });
    this.jobListener = new taskcluster.PulseListener({
      queueName: this.jobQueueName,
      connection: this.connection,
    });

    // Listen for new jobs created via the api webhook endpoint
    let GithubEvents = taskcluster.createClient(this.reference);
    let githubEvents = new GithubEvents();
    await this.jobListener.bind(githubEvents.pullRequest());
    await this.jobListener.bind(githubEvents.push());
    await this.jobListener.bind(githubEvents.release());

    // Listen for state changes to the taskcluster tasks and taskgroups
    // We only need to listen for failure and exception events on
    // tasks. We wait for the entire group to be resolved before checking
    // for success.
    let queueEvents = new taskcluster.QueueEvents();
    let schedulerId = this.context.cfg.taskcluster.schedulerId;
    await this.statusListener.bind(queueEvents.taskFailed({schedulerId}));
    await this.statusListener.bind(queueEvents.taskException({schedulerId}));
    await this.statusListener.bind(queueEvents.taskGroupResolved({schedulerId}));

    const callHandler = (name, handler) => message => {
      //console.log('.. message received by handler', message);
      handler.call(this, message).catch(async err => {
        debug(`Error (reported to sentry) while calling ${name} handler: ${err}`);
        await this.monitor.reportError(err);
        return err;
      }).then((err=null) => {
        if (this.handlerComplete && !err) {
          this.handlerComplete();
        } else if (this.handlerRejected && err) {
          this.handlerRejected(err);
        }
      });
    };

    this.jobListener.on('message',
      this.monitor.timedHandler('joblistener', callHandler('job', jobHandler)));
    this.statusListener.on('message',
      this.monitor.timedHandler('statuslistener', callHandler('status', statusHandler)));

    // If this is awaited, it should return [undefined, undefined]
    await Promise.all([this.jobListener.resume(), this.statusListener.resume()]);
  }

  async terminate() {
    debug('Terminating handlers...');
    if (this.connection) {
      await this.connection.close();
      this.connection = undefined;
    }
  }

  // Create a collection of tasks, centralized here to enable testing without creating tasks.
  async createTasks({scopes, tasks}) {
    let queue = new taskcluster.Queue({
      baseUrl: this.context.cfg.taskcluster.queueBaseUrl,
      credentials: this.context.cfg.taskcluster.credentials,
      authorizedScopes: scopes,
    });
    await Promise.all(tasks.map(t => queue.createTask(t.taskId, t.task)));
  }

  // Send an exception to Github in the form of a comment.
  async createExceptionComment({instGithub, organization, repository, sha, error}) {
    let errorBody = error.body && error.body.error || error.message;
    // Let's prettify any objects
    if (typeof errorBody == 'object') {
      errorBody = JSON.stringify(errorBody, null, 4);
    }

    // Warn the user know that there was a problem handling their request
    // by posting a comment; this error is then considered handled and not
    // reported to the taskcluster team or retried
    await instGithub.repos.createCommitComment({
      owner: organization,
      repo: repository,
      sha,
      body: [
        '\n',
        'Submitting the task to TaskCluster failed. Details\n\n',
        '```js\n',
        errorBody,
        '```\n',
        '',
      ].join('\n'),
    });
  }
}
module.exports = Handlers;

/**
 * Post updates to GitHub, when the status of a task changes.
 * TaskCluster States: https://docs.taskcluster.net/reference/platform/queue/references/events
 * GitHub Statuses: https://developer.github.com/v3/repos/statuses/
 **/
async function statusHandler(message) {
  let taskGroupId = message.payload.taskGroupId || message.payload.status.taskGroupId;

  let build = await this.context.Builds.load({
    taskGroupId,
  });

  let debug = Debug(debugPrefix + ':' + build.eventId);
  debug(`Handling state change for task-group ${taskGroupId}`);

  let state = 'success';

  if (message.exchange.endsWith('task-group-resolved')) {
    let queue = new taskcluster.Queue({
      baseUrl: this.context.cfg.taskcluster.queueBaseUrl,
    });
    let params = {};
    do {
      let group = await queue.listTaskGroup(message.payload.taskGroupId, params);
      params.continuationToken = group.continuationToken;
      group.tasks.forEach(task => {
        if (_.includes(['failed', 'exception'], task.status.state)) {
          state = 'failure';
        }
      });
    } while (params.continuationToken);
  }

  if (message.exchange.endsWith('task-exception') || message.exchange.endsWith('task-failed')) {
    state = 'failure';
  }

  await build.modify(b => {
    if (b.state !== 'failure') {
      b.state = state;
      b.updated = new Date();
    }
  });
  if (build.state !== state) {
    debug('Task group already marked as failure. Continuing.');
    return;
  }

  // Authenticating as installation.
  try {
    debug('Authenticating as installation in status handler...');
    var instGithub = await this.context.github.getInstallationGithub(build.installationId);
    debug('Authorized as installation in status handler');
  } catch (e) {
    debug(`Error authenticating as installation in status handler! Error: ${e}`);
    throw e;
  }

  debug(`Attempting to update status for ${build.organization}/${build.repository}@${build.sha} (${state})`);
  try {
    await instGithub.repos.createStatus({
      owner: build.organization,
      repo: build.repository,
      sha: build.sha,
      state,
      target_url: INSPECTOR_URL + taskGroupId,
      description: 'TaskGroup: ' + state,
      context: `${this.context.cfg.app.statusContext} (${build.eventType.split('.')[0]})`,
    });
  } catch (e) {
    debug(`Failed to update status: ${build.organization}/${build.repository}@${build.sha}`);
    throw e;
  }
}

/**
 * If a .taskcluster.yml exists, attempt to turn it into a taskcluster
 * graph config, and submit it to the scheduler.
 **/
async function jobHandler(message) {
  let debug = Debug(debugPrefix + ':' + message.payload.eventId);
  debug('Received message. Starting processing...');
  let context = this.context;

  // Authenticating as installation.
  let instGithub = await context.github.getInstallationGithub(message.payload.installationId);

  // We must attempt to convert the sanitized fields back to normal here.
  // Further discussion of how to deal with this cleanly is in
  // https://github.com/taskcluster/taskcluster-github/issues/52
  message.payload.organization = message.payload.organization.replace(/%/g, '.');
  message.payload.repository = message.payload.repository.replace(/%/g, '.');
  let organization = message.payload.organization;
  let repository = message.payload.repository;
  let sha = message.payload.details['event.head.sha'];
  //console.log('..message passed to jobHandler', message);
  let number = message.payload.details['event.pullNumber'];
  //console.log('..pr number', number);
  if (!sha) {
    debug('Trying to get commit info in job handler...');
    let commitInfo = await instGithub.repos.getShaOfCommitRef({
      owner: organization,
      repo: repository,
      ref: `refs/tags/${message.payload.details['event.version']}`,
    });
    sha = commitInfo.data.sha;
  }

  debug(`handling ${message.payload.details['event.type']} webhook for: ${organization}/${repository}@${sha}`);
  let repoconf = undefined;

  // Try to fetch a .taskcluster.yml file for every request
  try {
    debug('Trying to fetch the YML...');
    let tcyml = await instGithub.repos.getContent({
      owner: organization,
      repo: repository,
      path: '.taskcluster.yml',
      ref: sha,
    });
    repoconf = new Buffer(tcyml.data.content, 'base64').toString();
  } catch (e) {
    if (e.code === 404) {
      debug(`${organization}/${repository} has no '.taskcluster.yml'. Skipping.`);
      return;
    }
    if (_.endsWith(e.message, '\n\n') && e.message.length > 10000) {
      // We kept getting full html 500/400 pages from github in the logs.
      // I consider this to be a hard-to-fix bug in octokat, so let's make
      // the logs usable for now and try to fix this later. It's a relatively
      // rare occurence.
      debug('Detected an extremeley long error. Truncating!');
      e.message = _.join(_.take(e.message, 100).concat('...'), '');
      e.stack = e.stack.split('\n\n')[1] || e.stack;
    }
    throw e;
  }

  // Check if this is meant to be built by tc-github at all.
  // This is a bit of a hack, but is needed for bug 1274077 for now
  try {
    let c = yaml.safeLoad(repoconf);
    c.tasks = (Array.isArray(c.tasks) ? c.tasks : []).filter((task) => _.has(task, 'extra.github'));
    if (c.tasks.length === 0) {
      debug(`Skipping tasks in ${organization}/${repository} because no task with "extra.github" exists!`);
      return;
    }
  } catch (e) {
    if (e.name === 'YAMLException') {
      return await this.createExceptionComment({instGithub, organization, repository, sha, error: e});
    }
    throw e;
  }

  let groupState = 'pending';
  let taskGroupId = 'nonexistent';
  let graphConfig;

  // Now we can try processing the config and kicking off a task.
  try {
    graphConfig = this.intree({
      config: repoconf,
      payload: message.payload,
      validator: context.validator,
      schema: 'http://schemas.taskcluster.net/github/v1/taskcluster-github-config.json#',
    });
    if (graphConfig.tasks.length === 0) {
      debug(`intree config for ${organization}/${repository} compiled with zero tasks. Skipping.`);
      return;
    }
  } catch (e) {
    debug('.taskcluster.yml was not formatted correctly. Leaving comment on Github.');
    await this.createExceptionComment({instGithub, organization, repository, sha, error: e});
    return;
  }

  if (message.payload.details['event.type'].startsWith('pull_request.')) {
    debug('Checking pull request permission...');

    // Decide if a user has permissions to run tasks.
    let login = message.payload.details['event.head.user.login'];
    try {
      if (!await prAllowed({login, organization, repository, instGithub, debug, message})) {
        let body = [
          '\n',
          'No TaskCluster jobs started for this pull request\n\n',
          '```js\n',
          'The `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the',
          'default branch) does not allow starting tasks for this pull request.',
          '```\n',
          '',
        ].join('\n');
        //console.log(instGithub.pullRequests);
        //console.log('..pr number' organization);
        await instGithub.pullRequests.createComment({
          owner: organization,
          repo: repository,
          number,
          body,
        });
        return;
      }
    } catch (e) {
      if (e.name === 'YAMLException') {
        let docsLink = 'https://docs.taskcluster.net/reference/integrations/github/docs/usage#who-can-trigger-jobs';
        await instGithub.repos.createCommitComment({
          owner: organization,
          repo: repository,
          sha,
          body: [
            '\n',
            'Error in `.taskcluster.yml` while checking',
            'for permissions **on default branch ' + branch + '**.',
            'Read more about this in',
            '[the taskcluster docs](' + docsLink + ').',
            'Details\n\n',
            '```js\n',
            e.message,
            '```\n',
            '',
          ].join('\n'),
        });
        return;
      }
      throw e;
    }
  }

  try {
    taskGroupId = graphConfig.tasks[0].task.taskGroupId;
    debug(`Creating tasks. (taskGroupId: ${taskGroupId})`);
    await this.createTasks({scopes: graphConfig.scopes, tasks: graphConfig.tasks});
  } catch (e) {
    debug('Creating tasks failed! Leaving comment on Github.');
    groupState = 'failure';
    await this.createExceptionComment({instGithub, organization, repository, sha, error: e});
  } finally {
    debug(`Trying to create status for ${organization}/${repository}@${sha} (${groupState})`);
    let eventType = message.payload.details['event.type'];
    let statusContext = `${this.context.cfg.app.statusContext} (${eventType.split('.')[0]})`;
    let description = groupState === 'pending' ? `TaskGroup: Pending (for ${eventType})` : 'TaskGroup: Exception';
    await instGithub.repos.createStatus({
      owner: organization,
      repo: repository,
      sha,
      state: groupState,
      target_url: INSPECTOR_URL + taskGroupId,
      description,
      context: statusContext,
    });

    let now = new Date();
    await context.Builds.create({
      organization,
      repository,
      sha,
      taskGroupId,
      state: groupState,
      created: now,
      updated: now,
      installationId: message.payload.installationId,
      eventType: message.payload.details['event.type'],
      eventId: message.payload.eventId,
    }).catch(async (err) => {
      if (err.code !== 'EntityAlreadyExists') {
        throw err;
      }
      let build = await this.Builds.load({
        taskGroupId,
      });
      assert.equal(build.state, groupState, `State for ${organization}/${repository}@${sha}
        already exists but is set to ${build.state} instead of ${groupState}!`);
      assert.equal(build.organization, organization);
      assert.equal(build.repository, repository);
      assert.equal(build.sha, sha);
      assert.equal(build.eventType, message.payload.details['event.type']);
      assert.equal(build.eventId, message.payload.eventId);
    });
  }
}

<commit_msg>use pullRequests.createComment() when responding to a PR<commit_after>let Debug = require('debug');
let taskcluster = require('taskcluster-client');
let slugid = require('slugid');
let yaml = require('js-yaml');
let assert = require('assert');
let EventEmitter = require('events');
let _ = require('lodash');
let Promise = require('bluebird');
let prAllowed = require('./pr-allowed');

let INSPECTOR_URL = 'https://tools.taskcluster.net/task-group-inspector/#/';

let debugPrefix = 'taskcluster-github:handlers';
let debug = Debug(debugPrefix);

/**
 * Create handlers
 *
 * options:
 * {
 *   credentials:        // Pulse credentials
 *   queueName:          // Queue name (optional)
 *   reference:          // A taskcluster exchange reference for this service
 *   monitor:            // base.monitor({...})
 *   jobQueueName:       // An optional queue name for the jobs handler
 *   statusQueueName:    // An optional queue name for the status handler
 *   context:            // Things we want to make available inside handlers
 * }
 */
class Handlers {
  constructor({credentials, monitor, reference, jobQueueName, statusQueueName, intree, context}) {
    debug('Constructing handlers...');
    assert(monitor, 'monitor is required for statistics');
    assert(monitor, 'reference must be provided');
    assert(intree, 'intree configuration builder must be provided');
    this.credentials = credentials;
    this.monitor = monitor;
    this.reference = reference;
    this.intree = intree;
    this.connection = null;
    this.statusListener = null;
    this.jobListener = null;
    this.statusQueueName = statusQueueName;  // Optional
    this.jobQueueName = jobQueueName;  // Optional
    this.context = context;

    this.handlerComplete = null;
    this.handlerRejected = null;
  }

  /**
   * Set up the handlers.
   */
  async setup(options) {
    debug('Setting up handlers...');
    options = options || {};
    assert(!this.connection, 'Cannot setup twice!');
    this.connection = new taskcluster.PulseConnection(this.credentials);
    this.statusListener = new taskcluster.PulseListener({
      queueName: this.statusQueueName,
      connection: this.connection,
    });
    this.jobListener = new taskcluster.PulseListener({
      queueName: this.jobQueueName,
      connection: this.connection,
    });

    // Listen for new jobs created via the api webhook endpoint
    let GithubEvents = taskcluster.createClient(this.reference);
    let githubEvents = new GithubEvents();
    await this.jobListener.bind(githubEvents.pullRequest());
    await this.jobListener.bind(githubEvents.push());
    await this.jobListener.bind(githubEvents.release());

    // Listen for state changes to the taskcluster tasks and taskgroups
    // We only need to listen for failure and exception events on
    // tasks. We wait for the entire group to be resolved before checking
    // for success.
    let queueEvents = new taskcluster.QueueEvents();
    let schedulerId = this.context.cfg.taskcluster.schedulerId;
    await this.statusListener.bind(queueEvents.taskFailed({schedulerId}));
    await this.statusListener.bind(queueEvents.taskException({schedulerId}));
    await this.statusListener.bind(queueEvents.taskGroupResolved({schedulerId}));

    const callHandler = (name, handler) => message => {
      //console.log('.. message received by handler', message);
      handler.call(this, message).catch(async err => {
        debug(`Error (reported to sentry) while calling ${name} handler: ${err}`);
        await this.monitor.reportError(err);
        return err;
      }).then((err=null) => {
        if (this.handlerComplete && !err) {
          this.handlerComplete();
        } else if (this.handlerRejected && err) {
          this.handlerRejected(err);
        }
      });
    };

    this.jobListener.on('message',
      this.monitor.timedHandler('joblistener', callHandler('job', jobHandler)));
    this.statusListener.on('message',
      this.monitor.timedHandler('statuslistener', callHandler('status', statusHandler)));

    // If this is awaited, it should return [undefined, undefined]
    await Promise.all([this.jobListener.resume(), this.statusListener.resume()]);
  }

  async terminate() {
    debug('Terminating handlers...');
    if (this.connection) {
      await this.connection.close();
      this.connection = undefined;
    }
  }

  // Create a collection of tasks, centralized here to enable testing without creating tasks.
  async createTasks({scopes, tasks}) {
    let queue = new taskcluster.Queue({
      baseUrl: this.context.cfg.taskcluster.queueBaseUrl,
      credentials: this.context.cfg.taskcluster.credentials,
      authorizedScopes: scopes,
    });
    await Promise.all(tasks.map(t => queue.createTask(t.taskId, t.task)));
  }

  // Send an exception to Github in the form of a comment.
  async createExceptionComment({instGithub, organization, repository, sha, error, number}) {
    let errorBody = error.body && error.body.error || error.message;
    // Let's prettify any objects
    if (typeof errorBody == 'object') {
      errorBody = JSON.stringify(errorBody, null, 4);
    }
    let body = [
      '\n',
      'Submitting the task to TaskCluster failed. Details\n\n',
      '```js\n',
      errorBody,
      '```\n',
      '',
    ].join('\n') ;

    // Warn the user know that there was a problem handling their request
    // by posting a comment; this error is then considered handled and not
    // reported to the taskcluster team or retried
    if (number) {
      await instGithub.pullRequests.createComment({
        owner: organization,
        repo: repository,
        number,
        body,
      });
      return;
    }
    await instGithub.repos.createCommitComment({
      owner: organization,
      repo: repository,
      sha,
      body,
    });
  }
}
module.exports = Handlers;

/**
 * Post updates to GitHub, when the status of a task changes.
 * TaskCluster States: https://docs.taskcluster.net/reference/platform/queue/references/events
 * GitHub Statuses: https://developer.github.com/v3/repos/statuses/
 **/
async function statusHandler(message) {
  let taskGroupId = message.payload.taskGroupId || message.payload.status.taskGroupId;

  let build = await this.context.Builds.load({
    taskGroupId,
  });

  let debug = Debug(debugPrefix + ':' + build.eventId);
  debug(`Handling state change for task-group ${taskGroupId}`);

  let state = 'success';

  if (message.exchange.endsWith('task-group-resolved')) {
    let queue = new taskcluster.Queue({
      baseUrl: this.context.cfg.taskcluster.queueBaseUrl,
    });
    let params = {};
    do {
      let group = await queue.listTaskGroup(message.payload.taskGroupId, params);
      params.continuationToken = group.continuationToken;
      group.tasks.forEach(task => {
        if (_.includes(['failed', 'exception'], task.status.state)) {
          state = 'failure';
        }
      });
    } while (params.continuationToken);
  }

  if (message.exchange.endsWith('task-exception') || message.exchange.endsWith('task-failed')) {
    state = 'failure';
  }

  await build.modify(b => {
    if (b.state !== 'failure') {
      b.state = state;
      b.updated = new Date();
    }
  });
  if (build.state !== state) {
    debug('Task group already marked as failure. Continuing.');
    return;
  }

  // Authenticating as installation.
  try {
    debug('Authenticating as installation in status handler...');
    var instGithub = await this.context.github.getInstallationGithub(build.installationId);
    debug('Authorized as installation in status handler');
  } catch (e) {
    debug(`Error authenticating as installation in status handler! Error: ${e}`);
    throw e;
  }

  debug(`Attempting to update status for ${build.organization}/${build.repository}@${build.sha} (${state})`);
  try {
    await instGithub.repos.createStatus({
      owner: build.organization,
      repo: build.repository,
      sha: build.sha,
      state,
      target_url: INSPECTOR_URL + taskGroupId,
      description: 'TaskGroup: ' + state,
      context: `${this.context.cfg.app.statusContext} (${build.eventType.split('.')[0]})`,
    });
  } catch (e) {
    debug(`Failed to update status: ${build.organization}/${build.repository}@${build.sha}`);
    throw e;
  }
}

/**
 * If a .taskcluster.yml exists, attempt to turn it into a taskcluster
 * graph config, and submit it to the scheduler.
 **/
async function jobHandler(message) {
  let debug = Debug(debugPrefix + ':' + message.payload.eventId);
  debug('Received message. Starting processing...');
  let context = this.context;

  // Authenticating as installation.
  let instGithub = await context.github.getInstallationGithub(message.payload.installationId);

  // We must attempt to convert the sanitized fields back to normal here.
  // Further discussion of how to deal with this cleanly is in
  // https://github.com/taskcluster/taskcluster-github/issues/52
  message.payload.organization = message.payload.organization.replace(/%/g, '.');
  message.payload.repository = message.payload.repository.replace(/%/g, '.');
  let organization = message.payload.organization;
  let repository = message.payload.repository;
  let sha = message.payload.details['event.head.sha'];
  //console.log('..message passed to jobHandler', message);
  let number = message.payload.details['event.pullNumber'];
  //console.log('..pr number', number);
  if (!sha) {
    debug('Trying to get commit info in job handler...');
    let commitInfo = await instGithub.repos.getShaOfCommitRef({
      owner: organization,
      repo: repository,
      ref: `refs/tags/${message.payload.details['event.version']}`,
    });
    sha = commitInfo.data.sha;
  }

  debug(`handling ${message.payload.details['event.type']} webhook for: ${organization}/${repository}@${sha}`);
  let repoconf = undefined;

  // Try to fetch a .taskcluster.yml file for every request
  try {
    debug('Trying to fetch the YML...');
    let tcyml = await instGithub.repos.getContent({
      owner: organization,
      repo: repository,
      path: '.taskcluster.yml',
      ref: sha,
    });
    repoconf = new Buffer(tcyml.data.content, 'base64').toString();
  } catch (e) {
    if (e.code === 404) {
      debug(`${organization}/${repository} has no '.taskcluster.yml'. Skipping.`);
      return;
    }
    if (_.endsWith(e.message, '\n\n') && e.message.length > 10000) {
      // We kept getting full html 500/400 pages from github in the logs.
      // I consider this to be a hard-to-fix bug in octokat, so let's make
      // the logs usable for now and try to fix this later. It's a relatively
      // rare occurence.
      debug('Detected an extremeley long error. Truncating!');
      e.message = _.join(_.take(e.message, 100).concat('...'), '');
      e.stack = e.stack.split('\n\n')[1] || e.stack;
    }
    throw e;
  }

  // Check if this is meant to be built by tc-github at all.
  // This is a bit of a hack, but is needed for bug 1274077 for now
  try {
    let c = yaml.safeLoad(repoconf);
    c.tasks = (Array.isArray(c.tasks) ? c.tasks : []).filter((task) => _.has(task, 'extra.github'));
    if (c.tasks.length === 0) {
      debug(`Skipping tasks in ${organization}/${repository} because no task with "extra.github" exists!`);
      return;
    }
  } catch (e) {
    if (e.name === 'YAMLException') {
      return await this.createExceptionComment({instGithub, organization, repository, sha, error: e, number});
    }
    throw e;
  }

  let groupState = 'pending';
  let taskGroupId = 'nonexistent';
  let graphConfig;

  // Now we can try processing the config and kicking off a task.
  try {
    graphConfig = this.intree({
      config: repoconf,
      payload: message.payload,
      validator: context.validator,
      schema: 'http://schemas.taskcluster.net/github/v1/taskcluster-github-config.json#',
    });
    if (graphConfig.tasks.length === 0) {
      debug(`intree config for ${organization}/${repository} compiled with zero tasks. Skipping.`);
      return;
    }
  } catch (e) {
    debug('.taskcluster.yml was not formatted correctly. Leaving comment on Github.');
    await this.createExceptionComment({instGithub, organization, repository, sha, error: e, number});
    return;
  }

  if (message.payload.details['event.type'].startsWith('pull_request.')) {
    debug('Checking pull request permission...');

    // Decide if a user has permissions to run tasks.
    let login = message.payload.details['event.head.user.login'];
    try {
      if (!await prAllowed({login, organization, repository, instGithub, debug, message})) {
        let body = [
          '\n',
          'No TaskCluster jobs started for this pull request\n\n',
          '```js\n',
          'The `allowPullRequests` configuration for this repository (in `.taskcluster.yml` on the',
          'default branch) does not allow starting tasks for this pull request.',
          '```\n',
          '',
        ].join('\n');
        //console.log(instGithub.pullRequests);
        //console.log('..pr number' organization);
        await instGithub.pullRequests.createComment({
          owner: organization,
          repo: repository,
          number,
          body,
        });
        return;
      }
    } catch (e) {
      if (e.name === 'YAMLException') {
        let docsLink = 'https://docs.taskcluster.net/reference/integrations/github/docs/usage#who-can-trigger-jobs';
        await instGithub.pullRequests.createComment({
          owner: organization,
          repo: repository,
          number,
          body: [
            '\n',
            'Error in `.taskcluster.yml` while checking',
            'for permissions **on default branch ' + branch + '**.',
            'Read more about this in',
            '[the taskcluster docs](' + docsLink + ').',
            'Details\n\n',
            '```js\n',
            e.message,
            '```\n',
            '',
          ].join('\n'),
        });
        return;
      }
      throw e;
    }
  }

  try {
    taskGroupId = graphConfig.tasks[0].task.taskGroupId;
    debug(`Creating tasks. (taskGroupId: ${taskGroupId})`);
    await this.createTasks({scopes: graphConfig.scopes, tasks: graphConfig.tasks});
  } catch (e) {
    debug('Creating tasks failed! Leaving comment on Github.');
    groupState = 'failure';
    await this.createExceptionComment({instGithub, organization, repository, sha, error: e});
  } finally {
    debug(`Trying to create status for ${organization}/${repository}@${sha} (${groupState})`);
    let eventType = message.payload.details['event.type'];
    let statusContext = `${this.context.cfg.app.statusContext} (${eventType.split('.')[0]})`;
    let description = groupState === 'pending' ? `TaskGroup: Pending (for ${eventType})` : 'TaskGroup: Exception';
    await instGithub.repos.createStatus({
      owner: organization,
      repo: repository,
      sha,
      state: groupState,
      target_url: INSPECTOR_URL + taskGroupId,
      description,
      context: statusContext,
    });

    let now = new Date();
    await context.Builds.create({
      organization,
      repository,
      sha,
      taskGroupId,
      state: groupState,
      created: now,
      updated: now,
      installationId: message.payload.installationId,
      eventType: message.payload.details['event.type'],
      eventId: message.payload.eventId,
    }).catch(async (err) => {
      if (err.code !== 'EntityAlreadyExists') {
        throw err;
      }
      let build = await this.Builds.load({
        taskGroupId,
      });
      assert.equal(build.state, groupState, `State for ${organization}/${repository}@${sha}
        already exists but is set to ${build.state} instead of ${groupState}!`);
      assert.equal(build.organization, organization);
      assert.equal(build.repository, repository);
      assert.equal(build.sha, sha);
      assert.equal(build.eventType, message.payload.details['event.type']);
      assert.equal(build.eventId, message.payload.eventId);
    });
  }
}

<|endoftext|>


<commit_before>/*
	hotlinkr
	Version: 0.1
	Copyright 2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://github.com/digitalBush/hotlinkr/raw/master/LICENSE)

	If you are reading this because you were hotlinking to a script hoted on another site, then shame on you.
	Please go get the script you need and host it from your own server using you own bandwidth.

	If you're not sure why what you're doing is wrong, please visit http://altlab.com/hotlinking.html
*/

(function(type,url) {
	var body=null;

	function doTags(tag,fn){
		var elms=document.getElementsByTagName(tag);
		for (var i = -1, len = elms.length; ++i < len; )
			fn.call(elms[i]);
	}

	var types={
		alert:function(){alert("Hotlinking is bad.  Please stop hotlinking to "+url);},
		redirect: function(){window.location.href="http://"+url;},
		link:function(){doTags("a",function(){this.href="http://"+url;});},
		hide:function(){body.style.display="none";},
		banner:function(){
			var banner=document.createElement("div");
			banner.setAttribute("style","position:absolute;top:0;left:0;width:100%;height:100px;border:1px solid #f00;line-height:100px;text-align:center;color:#f00;background:#fff");
			banner.innerText="Script Hosting provided courtesy of "+url+"
To remove this message, please host scripts from "+url+" on your own server.";
			body.appendChild(banner);
			body.style.paddingTop="100px";
		},
		cornify:function(){
			var s=document.createElement('script');
			s.src='http://cornify.com/js/cornify.js';
			body.appendChild(s);

			setInterval(function(){cornify_add();},5000);
		},
		translate:function(){
			window.location.href="http://translate.google.com/translate?tl=sw&u="+window.location.href;
		},
		pad:function(){doTags("div",function(){this.style.padding="300px";});},
		title:function(){document.title=url+" is my daddy!"},
		hasselhoff:function(){doTags("img",function(){this.src="http://farm1.static.flickr.com/54/127503978_0c1a5a0f4b_t.jpg"})},
		overlay:function(){
			var banner=document.createElement("div");
			banner.setAttribute("style",'position:absolute;top:0;width:100%;height:100%;right:0;z-index:1000;');
			body.appendChild(banner);
		},
		flip:function(){
			doTags("img",function(){this.setAttribute('style',this.getAttribute('style')+";-moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);transform: scaleY(-1);filter: FlipV;")});
		}
	};

	if(type=="random"){
		var arr=[];
		for(var key in types){
			if(types.hasOwnProperty(key))
				arr.push(key);
		}
		var rand=Math.floor(Math.random() * arr.length);
		type=arr[rand];
	}

	window.onload=function(){
		body=document.getElementsByTagName("body")[0];
		type=type.split(',');
		for(var i=-1,len=type.length;++i<len;)
			types[type[i]]();
	};
})("random","digitalbush.com");<commit_msg>added fail(), spaz(), and resize() types. optimized how functions are chosen and changed code to use addEventListener and attachEvent instead of onload.<commit_after>/*
	hotlinkr
	Version: 0.1
	Copyright 2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://github.com/digitalBush/hotlinkr/raw/master/LICENSE)

	If you are reading this because you were hotlinking to a script hoted on another site, then shame on you.
	Please go get the script you need and host it from your own server using you own bandwidth.

	If you're not sure why what you're doing is wrong, please visit http://altlab.com/hotlinking.html
*/

(function(type,url) {
	var body=null;

	function doTags(tag,fn){
		var elms=document.getElementsByTagName(tag);
		for (var i = -1, len = elms.length; ++i < len; )
			fn.call(elms[i]);
	}

	var isIE=(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) );

	function chooseRandomly(obj,seed) {
		var index = -1, item = null;
		if( !seed ) {
			seed=[];
			for(var key in obj){
				if(obj.hasOwnProperty(key))
					seed.push(key);
			}
		}
		index=Math.floor(Math.random() * seed.length);
		if(console && console.log)console.log("hotlinkr >> type = " + seed[index]);
		return obj[seed[index]];
	}

	var types={
		alert:function(){alert("Hotlinking is bad.  Please stop hotlinking to "+url);},
		redirect: function(){window.location.href="http://"+url;},
		link:function(){doTags("a",function(){this.href="http://"+url;});},
		hide:function(){body.style.display="none";},
		banner:function(){
			var banner=document.createElement("div");
			banner.setAttribute("style","position:absolute;top:0;left:0;width:100%;height:100px;border:1px solid #f00;line-height:100px;text-align:center;color:#f00;background:#fff");
			banner.innerText="Script Hosting provided courtesy of "+url+"
To remove this message, please host scripts from "+url+" on your own server.";
			body.appendChild(banner);
			body.style.paddingTop="100px";
		},
		cornify:function(){
			var s=document.createElement('script');
			s.src='http://cornify.com/js/cornify.js';
			body.appendChild(s);

			setInterval(function(){cornify_add();},5000);
		},
		translate:function(){
			window.location.href="http://translate.google.com/translate?tl=sw&u="+window.location.href;
		},
		pad:function(){doTags("div",function(){this.style.padding="300px";});},
		title:function(){document.title=url+" is my daddy!"},
		hasselhoff:function(){doTags("img",function(){this.src="http://farm1.static.flickr.com/54/127503978_0c1a5a0f4b_t.jpg"})},
		overlay:function(){
			var banner=document.createElement("img");
			banner.setAttribute("style",'position:absolute;top:0;width:100%;height:' + window.outerHeight + 'px;right:0;z-index:1000;');
			body.appendChild(banner);
		},
		flip:function(){
			if( !isIE ) {
				body.setAttribute('style',body.getAttribute('style') + "; -moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);transform: scaleY(-1);filter: FlipV;");
			} else {
				doTags("img",function() {
					this.setAttribute('style', this.getAttribute('style') + "; -moz-transform: scaleY(-1);-webkit-transform: scaleY(-1);transform: scaleY(-1);filter: FlipV;");
				});
			}
		},
		fail:function() {
			window.location.href = "http://farm4.static.flickr.com/3102/2780516431_28b7d23cfc.jpg";
		}

	};

	if( isIE ) {
		types.resize=function(x,y) {
			if(x && y) {
				window.resizeBy(x,y);
			}else {
				var local = types, v = new Date().getUTCMilliseconds()%2 == 0 ? -1 : 1;
				setInterval( function() { try{local.resize(v,v);}catch(e){} }, 10);
			}
		};
		types.spaz=function() {
			var local = types;
			setInterval( function() { var v = new Date().getUTCMilliseconds()%2 == 0 ? -1 : 1; try{local.resize(v,v);}catch(e){} }, 1);
		};
	}

	(window.addEventListener ? window.addEventListener : window.attachEvent)(isIE()?"onload":"load",function(){
		body=document.body?document.body:document.getElementsByTagName("body")[0];
		(chooseRandomly( types, type === "random" ? undefined : type.split(',') ))();
	}, false);
})("random","digitalbush.com");<|endoftext|>