<commit_before>/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */
const s3 = require('s3');
const request = require('request');
const Promise = require('bluebird');
const path = require('path');
const fs = require('fs-plus');

let s3Client = null;
let packageVersion = null;
let fullVersion = null;

module.exports = (grunt) => {
  const {spawn} = require('./task-helpers')(grunt);

  function populateVersion() {
    return new Promise((resolve, reject) => {
      const json = grunt.config.get('appJSON')
      const cmd = 'git';
      const args = ['rev-parse', '--short', 'HEAD'];
      spawn({cmd, args}, (error, {stdout} = {}) => {
        if (error) {
          return reject();
        }
        const commitHash = stdout ? stdout.trim() : null;
        packageVersion = json.version;
        if (packageVersion.indexOf('-') > 0) {
          fullVersion = packageVersion;
        } else {
          fullVersion = `${packageVersion}-${commitHash}`;
        }
        return resolve();
      });
    });
  }

  function postToSlack(msg) {
    if (!process.env.NYLAS_INTERNAL_HOOK_URL) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) =>
      request.post({
        url: process.env.NYLAS_INTERNAL_HOOK_URL,
        json: {
          username: "Edgehill Builds",
          text: msg,
        },
      }
      , (error) => {
        return error ? reject(error) : resolve();
      })
    );
  }

  function put(localSource, destName, options = {}) {
    grunt.log.writeln(`>> Uploading ${localSource} to S3…`);

    const write = grunt.log.writeln;
    let lastPc = 0;

    const params = {
      Key: destName,
      ACL: "public-read",
      Bucket: "edgehill",
    };
    Object.assign(params, options);

    return new Promise((resolve, reject) => {
      const uploader = s3Client.uploadFile({
        localFile: localSource,
        s3Params: params,
      });
      uploader.on("error", err => reject(err));
      uploader.on("progress", () => {
        const pc = Math.round((uploader.progressAmount / uploader.progressTotal) * 100.0);
        if (pc !== lastPc) {
          lastPc = pc;
          write(`>> Uploading ${destName} ${pc}%`);
          return;
        }
      });
      uploader.on("end", data => resolve(data));
    });
  }

  function uploadToS3(filename, key) {
    const filepath = path.join(grunt.config.get('outputDir'), filename);

    grunt.log.writeln(`>> Uploading ${filename} to ${key}…`);
    return put(filepath, key).then((data) => {
      const msg = `N1 release asset uploaded: <${data.Location}|${key}>`;
      return postToSlack(msg).then(() => Promise.resolve(data));
    });
  }

  grunt.registerTask("publish", "Publish Nylas build", function publish() {
    const done = this.async();

    populateVersion().then(() => {
      // find files to publish
      const {shouldPublishBuild} = require('./task-helpers')(grunt);
      const outputDir = grunt.config.get('outputDir');
      const uploads = [];

      if (process.platform === 'darwin') {
        uploads.push({
          source: `${outputDir}/N1.zip`,
          key: `${fullVersion}/${process.platform}/${process.arch}/N1.zip`,
        });
      } else if (process.platform === 'win32') {
        uploads.push({
          source: `${outputDir}/RELEASES`,
          key: `${fullVersion}/${process.platform}/${process.arch}/RELEASES`,
        });
        uploads.push({
          source: `${outputDir}/N1Setup.exe`,
          key: `${fullVersion}/${process.platform}/${process.arch}/N1Setup.exe`,
        });
        uploads.push({
          source: `${outputDir}/Nylas-${packageVersion}-full.nupkg`,
          key: `${fullVersion}/${process.platform}/${process.arch}/nylas-${packageVersion}-full.nupkg`,
        });
      } else if (process.platform === 'linux') {
        const files = fs.readdirSync(outputDir);
        for (const file of files) {
          if (path.extname(file) === '.deb') {
            uploads.push({
              source: file,
              key: `${fullVersion}/${process.platform}-deb/${process.arch}/N1.deb`,
              options: {ContentType: "application/x-deb"},
            });
          }
          if (path.extname(file) === '.rpm') {
            uploads.push({
              source: file,
              key: `${fullVersion}/${process.platform}-rpm/${process.arch}/N1.rpm`,
              options: {ContentType: "application/x-rpm"},
            });
          }
        }
      } else {
        grunt.fail.fatal(`Unsupported platform: '${process.platform}'`);
      }

      // configure environment
      if (!shouldPublishBuild()) {
        grunt.log.writeln(`>> Not publishing builds…`);
        grunt.log.writeln(`>> Would have uploaded the following assets: ${JSON.stringify(uploads, null, 2)}`);
        return;
      }
      const awsKey = process.env.AWS_ACCESS_KEY_ID != null ? process.env.AWS_ACCESS_KEY_ID : "";
      const awsSecret = process.env.AWS_SECRET_ACCESS_KEY != null ? process.env.AWS_SECRET_ACCESS_KEY : "";

      if (awsKey.length === 0) {
        grunt.fail.fatal("Please set the AWS_ACCESS_KEY_ID environment variable");
      }
      if (awsSecret.length === 0) {
        grunt.fail.fatal("Please set the AWS_SECRET_ACCESS_KEY environment variable");
      }

      s3Client = s3.createClient({
        s3Options: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          scretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        },
      });

      Promise.all(uploads.map(({source, key, options}) =>
        uploadToS3(source, key, options))
      ).then(done, done);
    });
  });
}
<commit_msg>fix(build) Fix output directory for builds<commit_after>/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */
const s3 = require('s3');
const request = require('request');
const Promise = require('bluebird');
const path = require('path');
const fs = require('fs-plus');

let s3Client = null;
let packageVersion = null;
let fullVersion = null;

module.exports = (grunt) => {
  const {spawn} = require('./task-helpers')(grunt);

  function populateVersion() {
    return new Promise((resolve, reject) => {
      const json = grunt.config.get('appJSON')
      const cmd = 'git';
      const args = ['rev-parse', '--short', 'HEAD'];
      spawn({cmd, args}, (error, {stdout} = {}) => {
        if (error) {
          return reject();
        }
        const commitHash = stdout ? stdout.trim() : null;
        packageVersion = json.version;
        if (packageVersion.indexOf('-') > 0) {
          fullVersion = packageVersion;
        } else {
          fullVersion = `${packageVersion}-${commitHash}`;
        }
        return resolve();
      });
    });
  }

  function postToSlack(msg) {
    if (!process.env.NYLAS_INTERNAL_HOOK_URL) {
      return Promise.resolve();
    }
    return new Promise((resolve, reject) =>
      request.post({
        url: process.env.NYLAS_INTERNAL_HOOK_URL,
        json: {
          username: "Edgehill Builds",
          text: msg,
        },
      }
      , (error) => {
        return error ? reject(error) : resolve();
      })
    );
  }

  function put(localSource, destName, options = {}) {
    grunt.log.writeln(`>> Uploading ${localSource} to S3…`);

    const write = grunt.log.writeln;
    let lastPc = 0;

    const params = {
      Key: destName,
      ACL: "public-read",
      Bucket: "edgehill",
    };
    Object.assign(params, options);

    return new Promise((resolve, reject) => {
      const uploader = s3Client.uploadFile({
        localFile: localSource,
        s3Params: params,
      });
      uploader.on("error", err => reject(err));
      uploader.on("progress", () => {
        const pc = Math.round((uploader.progressAmount / uploader.progressTotal) * 100.0);
        if (pc !== lastPc) {
          lastPc = pc;
          write(`>> Uploading ${destName} ${pc}%`);
          return;
        }
      });
      uploader.on("end", data => resolve(data));
    });
  }

  function uploadToS3(filepath, key) {
    grunt.log.writeln(`>> Uploading ${filename} to ${key}…`);
    return put(filepath, key).then((data) => {
      const msg = `N1 release asset uploaded: <${data.Location}|${key}>`;
      return postToSlack(msg).then(() => Promise.resolve(data));
    });
  }

  grunt.registerTask("publish", "Publish Nylas build", function publish() {
    const done = this.async();

    populateVersion().then(() => {
      // find files to publish
      const {shouldPublishBuild} = require('./task-helpers')(grunt);
      const outputDir = grunt.config.get('outputDir');
      const uploads = [];

      if (process.platform === 'darwin') {
        uploads.push({
          source: `${outputDir}/N1.zip`,
          key: `${fullVersion}/${process.platform}/${process.arch}/N1.zip`,
        });
      } else if (process.platform === 'win32') {
        uploads.push({
          source: `${outputDir}/RELEASES`,
          key: `${fullVersion}/${process.platform}/${process.arch}/RELEASES`,
        });
        uploads.push({
          source: `${outputDir}/N1Setup.exe`,
          key: `${fullVersion}/${process.platform}/${process.arch}/N1Setup.exe`,
        });
        uploads.push({
          source: `${outputDir}/Nylas-${packageVersion}-full.nupkg`,
          key: `${fullVersion}/${process.platform}/${process.arch}/nylas-${packageVersion}-full.nupkg`,
        });
      } else if (process.platform === 'linux') {
        const files = fs.readdirSync(outputDir);
        for (const file of files) {
          if (path.extname(file) === '.deb') {
            uploads.push({
              source: file,
              key: `${fullVersion}/${process.platform}-deb/${process.arch}/N1.deb`,
              options: {ContentType: "application/x-deb"},
            });
          }
          if (path.extname(file) === '.rpm') {
            uploads.push({
              source: file,
              key: `${fullVersion}/${process.platform}-rpm/${process.arch}/N1.rpm`,
              options: {ContentType: "application/x-rpm"},
            });
          }
        }
      } else {
        grunt.fail.fatal(`Unsupported platform: '${process.platform}'`);
      }

      // configure environment
      if (!shouldPublishBuild()) {
        grunt.log.writeln(`>> Not publishing builds…`);
        grunt.log.writeln(`>> Would have uploaded the following assets: ${JSON.stringify(uploads, null, 2)}`);
        return;
      }
      const awsKey = process.env.AWS_ACCESS_KEY_ID != null ? process.env.AWS_ACCESS_KEY_ID : "";
      const awsSecret = process.env.AWS_SECRET_ACCESS_KEY != null ? process.env.AWS_SECRET_ACCESS_KEY : "";

      if (awsKey.length === 0) {
        grunt.fail.fatal("Please set the AWS_ACCESS_KEY_ID environment variable");
      }
      if (awsSecret.length === 0) {
        grunt.fail.fatal("Please set the AWS_SECRET_ACCESS_KEY environment variable");
      }

      s3Client = s3.createClient({
        s3Options: {
          accessKeyId: process.env.AWS_ACCESS_KEY_ID,
          scretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        },
      });

      Promise.all(uploads.map(({source, key, options}) =>
        uploadToS3(source, key, options))
      ).then(done, done);
    });
  });
}
<|endoftext|>


<commit_before>module.exports = function(grunt, config, parameters, done) {

	var fs = require('fs');
	var execFile = require('child_process').execFile;
	var https = require('https');
	var path = require('path');
	var StringDecoder = require('string_decoder').StringDecoder;
	var decoder = new StringDecoder('utf8');

	function mkdir(dir, mode) {
		try {
			fs.mkdirSync(dir, mode);
		}
		catch(e) {
			if(e.errno !== 34) {
				throw e;
			}
			mkdir(path.dirname(dir), mode);
			mkdir(dir, mode);
		}
	}

	function createFile(filename) {
		filename = path.normalize(filename);
		var dir = path.dirname(filename);
		if(!fs.existsSync(dir)) {
			mkdir(dir);
		}
		return fs.openSync(filename, 'w');
	}

	function get(urlPath, options, callback) {
		https.get(
			{
				hostname: 'www.transifex.com',
				path: urlPath,
				auth: parameters.txUsername + ':' + parameters.txPassword
			},
			function(response) {
				var data = [], fd = null, cleanup = function() {};
				var goodResponse = response.statusCode < 300;
				if(goodResponse) {
					switch(options.type) {
						case 'file':
							try {
								fd = createFile(options.filename);
							}
							catch(e) {
								process.stderr.write('Error creating file ' + options.filename + ': ' + e.message + '\n');
								done(false);
								return;
							}
							cleanup = function(ok) {
								try {
									fs.closeSync(fd);
								}
								catch(foo) {
								}
								if(ok === false) {
									try {
										fs.unlinkSync(options.filename);
									}
									catch(foo) {
									}
								}
							}
							break;
					}
				}
				response
					.on('data', function(chunk) {
						if(fd) {
							try {
								fs.writeSync(fd, decoder.write(chunk));
							}
							catch(e) {
								cleanup(false);
								process.stderr.write('Error saving to file ' + options.filename + ': ' + e.message + '\n');
								done(false);
								return;
							}
						}
						else {
							data.push(chunk)
						}
					})
					.on('end', function() {
						if(!goodResponse) {
							cleanup(false);
							var msg = ''
							if(response.headers['content-type'] == 'text/plain') {
								msg = data.join('');
							}
							process.stderr.write('Error retrieving ' + urlPath + ': ' + (msg || response.statusCode) + '\n');
							done(false);
							return;
						}
						switch(options.type) {
							case 'file':
								cleanup(true);
								callback();
								break;
							case 'json':
								try {
									data = JSON.parse(data.join(''));
								}
								catch(e) {
									process.stderr.write('Error parsing response from ' + urlPath + ': ' + e.message + '\n');
									done(false);
									return;
								}
								callback(data);
								break;
						}
					})
				;
			}
		)
		.on('error', function(e) {
			process.stderr.write(e.message + '\n');
			done(false);
		})
	}

	if(!parameters.txUsername) {
		process.stderr.write('Transifex username not defined. Define a txUsername variable in Gruntfile.parameters.js file or use a --txUsername=... command line parameter.\n');
		done(false);
		return;
	}
	if(!parameters.txPassword) {
		process.stderr.write('Transifex password not defined. Define a txPassword variable in Gruntfile.parameters.js file or use a --txPassword=... command line parameter.\n');
		done(false);
		return;
	}
	if(!parameters.txResource) {
		process.stderr.write('Transifex resource not defined. Define a txResource variable in Gruntfile.parameters.js file or use a --txResource=... command line parameter.\n');
		done(false);
		return;
	}

	var txProgressLimit = parseFloat(parameters.txProgressLimit);
	if(isNaN(txProgressLimit)) {
		txProgressLimit = 90;
	}

	var getAllLocales;
	if(parameters.txLocales && parameters.txLocales.length) {
		getAllLocales = function(callback) {
			var cfgLocales = [];
			var txLocales = (typeof parameters.txLocales == 'string') ? parameters.txLocales.split(',') : parameters.txLocales;
			txLocales.forEach(function(code) {
				cfgLocales.push({code: code, name: code});
			});
			callback(cfgLocales);
		}
	}
	else {
		getAllLocales = function(callback) {
			process.stdout.write('Retrieving available locales for Transifex resource ' + parameters.txResource + '... ');
			get('/api/2/project/concrete5/resource/' + parameters.txResource + '/?details', {type: 'json'}, function(data) {
				var allLocales = [];
				data.available_languages.forEach(function(available_language) {
					switch(available_language.code) {
						case 'en': // Transifex returns this too
							break;
						default:
							allLocales.push({code: available_language.code, name: available_language.code + ' [' + available_language.name + ']'});
							break;
					}
				});
				process.stdout.write(allLocales.length + ' locales found.\n');
				callback(allLocales);
			});
		};
	}

	function downloadLocale(locale, callback) {
		process.stdout.write('\tdownloading .po file... ');
		get(
			'/api/2/project/concrete5/resource/' + parameters.txResource + '/translation/' + locale.code + '/?file',
			{type: 'file', filename: locale.poFile},
			function() {
				process.stdout.write('done.\n');
				callback();
			}
		);
	}

	function compileLocale(locale, callback) {
		process.stdout.write('\tcompiling .mo file... ');
		execFile(
			'msgfmt', ['-o', locale.moFile, locale.poFile],
			{},
			function(error, stdout, stderr) {
				if(error !== null) {
					process.stderr.write(error.message || error);
					done(false);
					return;
				}
				process.stdout.write('done.\n');
				callback();
			}
		);
	}

	function parseLocale(allLocales, localeIndex, callback) {
		if(localeIndex >= allLocales.length) {
			callback();
			return;
		}
		var locale = allLocales[localeIndex];
		var destinationFolder;

		if (parameters.destination) {
			destinationFolder = path.resolve(__dirname, '..', parameters.destination);
		} else {
			destinationFolder = path.resolve(__dirname, config.DIR_BASE);
		}

		process.stdout.write('Locale ' + locale.name + '... ');
		get('/api/2/project/concrete5/resource/' + parameters.txResource + '/stats/' + locale.code + '/', {type: 'json'}, function(data) {
			var tot = data.translated_entities + data.untranslated_entities;
			locale.percentage = tot ? Math.round(data.translated_entities * 100 / tot) : 0;
			locale.passed = (locale.percentage >= txProgressLimit);
			process.stdout.write(' progress: ' + locale.percentage + '% -> ' + (locale.passed ? 'ok' : ('skipped (less than ' + txProgressLimit + '%)')) + ' \n');
			if(!locale.passed) {
				parseLocale(allLocales, localeIndex + 1, callback);
				return;
			}
			locale.poFile = path.join(destinationFolder, 'application/languages/' + locale.code + '/LC_MESSAGES/messages.po');
			locale.moFile = path.join(destinationFolder, 'application/languages/' + locale.code + '/LC_MESSAGES/messages.mo');
			downloadLocale(locale, function() {
				compileLocale(
					locale,
					function() {
						process.stdout.write('\tdeleting .po file... ');
						try {
							fs.unlinkSync(locale.poFile);
						}
						catch(e) {
							process.stderr.write(e.message || e);
							done(false);
							return;
						}
						process.stdout.write('done.\n');
						parseLocale(allLocales, localeIndex + 1, callback);
					}
				);
			});
		});
	}

	getAllLocales(function(allLocales) {
		parseLocale(allLocales, 0, function() {
			done(true);
		});
	});

};
<commit_msg>Fix wrong plural forms count for some languages (Belarusian, Polish, Russian, Ukrainian)<commit_after>/* jshint unused:vars, undef:true, node:true */

module.exports = function(grunt, config, parameters, done) {

	var fs = require('fs');
	var execFile = require('child_process').execFile;
	var https = require('https');
	var path = require('path');
	var StringDecoder = require('string_decoder').StringDecoder;
	var decoder = new StringDecoder('utf8');
	var fixPluralRules = {
		// Belarusian
		'be': {
			pluralForms: 3,
			pluralFormula: 'n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2'
		},
		// Polish
		'pl': {
			pluralForms: 3,
			pluralFormula: 'n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2'
		},
		// Russian
		'ru': {
			pluralForms: 3,
			pluralFormula: 'n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2'
		},
		// Ukrainian
		'uk': {
			pluralForms: 3,
			pluralFormula: 'n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2'
		}
	};

	function mkdir(dir, mode) {
		try {
			fs.mkdirSync(dir, mode);
		}
		catch(e) {
			if(e.errno !== 34 && e.errno !== -4058) {
				throw e;
			}
			mkdir(path.dirname(dir), mode);
			mkdir(dir, mode);
		}
	}

	function createFile(filename) {
		filename = path.normalize(filename);
		var dir = path.dirname(filename);
		if(!fs.existsSync(dir)) {
			mkdir(dir);
		}
		return fs.openSync(filename, 'w');
	}

	function get(urlPath, options, callback) {
		https.get(
			{
				hostname: 'www.transifex.com',
				path: urlPath,
				auth: parameters.txUsername + ':' + parameters.txPassword
			},
			function(response) {
				var data = [], fd = null, cleanup = function() {};
				var goodResponse = response.statusCode < 300;
				if(goodResponse) {
					switch(options.type) {
						case 'file':
							try {
								fd = createFile(options.filename);
							}
							catch(e) {
								process.stderr.write('Error creating file ' + options.filename + ': ' + e.message + '\n');
								done(false);
								return;
							}
							cleanup = function(ok) {
								try {
									fs.closeSync(fd);
								}
								catch(foo) {
								}
								if(ok === false) {
									try {
										fs.unlinkSync(options.filename);
									}
									catch(foo) {
									}
								}
							};
							break;
					}
				}
				response
					.on('data', function(chunk) {
						if(fd) {
							try {
								fs.writeSync(fd, decoder.write(chunk));
							}
							catch(e) {
								cleanup(false);
								process.stderr.write('Error saving to file ' + options.filename + ': ' + e.message + '\n');
								done(false);
								return;
							}
						}
						else {
							data.push(chunk);
						}
					})
					.on('end', function() {
						if(!goodResponse) {
							cleanup(false);
							var msg = '';
							if(response.headers['content-type'] == 'text/plain') {
								msg = data.join('');
							}
							process.stderr.write('Error retrieving ' + urlPath + ': ' + (msg || response.statusCode) + '\n');
							done(false);
							return;
						}
						switch(options.type) {
							case 'file':
								cleanup(true);
								callback();
								break;
							case 'json':
								try {
									data = JSON.parse(data.join(''));
								}
								catch(e) {
									process.stderr.write('Error parsing response from ' + urlPath + ': ' + e.message + '\n');
									done(false);
									return;
								}
								callback(data);
								break;
						}
					})
				;
			}
		)
		.on('error', function(e) {
			process.stderr.write(e.message + '\n');
			done(false);
		});
	}

	if(!parameters.txUsername) {
		process.stderr.write('Transifex username not defined. Define a txUsername variable in Gruntfile.parameters.js file or use a --txUsername=... command line parameter.\n');
		done(false);
		return;
	}
	if(!parameters.txPassword) {
		process.stderr.write('Transifex password not defined. Define a txPassword variable in Gruntfile.parameters.js file or use a --txPassword=... command line parameter.\n');
		done(false);
		return;
	}
	if(!parameters.txResource) {
		process.stderr.write('Transifex resource not defined. Define a txResource variable in Gruntfile.parameters.js file or use a --txResource=... command line parameter.\n');
		done(false);
		return;
	}

	var txProgressLimit = parseFloat(parameters.txProgressLimit);
	if(isNaN(txProgressLimit)) {
		txProgressLimit = 90;
	}

	var getAllLocales;
	if(parameters.txLocales && parameters.txLocales.length) {
		getAllLocales = function(callback) {
			var cfgLocales = [];
			var txLocales = (typeof parameters.txLocales == 'string') ? parameters.txLocales.split(',') : parameters.txLocales;
			txLocales.forEach(function(code) {
				cfgLocales.push({code: code, name: code});
			});
			callback(cfgLocales);
		};
	}
	else {
		getAllLocales = function(callback) {
			process.stdout.write('Retrieving available locales for Transifex resource ' + parameters.txResource + '... ');
			get('/api/2/project/concrete5/resource/' + parameters.txResource + '/?details', {type: 'json'}, function(data) {
				var allLocales = [];
				data.available_languages.forEach(function(available_language) {
					switch(available_language.code) {
						case 'en': // Transifex returns this too
							break;
						default:
							allLocales.push({code: available_language.code, name: available_language.code + ' [' + available_language.name + ']'});
							break;
					}
				});
				process.stdout.write(allLocales.length + ' locales found.\n');
				callback(allLocales);
			});
		};
	}

	function downloadLocale(locale, callback) {
		process.stdout.write('\tdownloading .po file... ');
		get(
			'/api/2/project/concrete5/resource/' + parameters.txResource + '/translation/' + locale.code + '/?file',
			{type: 'file', filename: locale.poFile},
			function() {
				var match;
				process.stdout.write('done.\n');
				var fixRules = null;
				if (fixPluralRules.hasOwnProperty(locale.code.toLowerCase())) {
					fixRules = fixPluralRules[locale.code.toLowerCase()];
				} else {
					match = locale.code.match(/^(\w+)[_\-]/);
					if (match && fixPluralRules.hasOwnProperty(match[1].toLowerCase())) {
						fixRules = fixPluralRules[match[1].toLowerCase()];
					}
				}
				if (fixRules === null) {
					callback();
					return;
				}
				process.stdout.write('\tfixing plural rules... ');
				var po = fs.readFileSync(locale.poFile, 'utf8');
				po = po.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
				match = po.match(/\n"Plural-Forms:\s*nplurals\s*=\s*(\d+)\s*; plural\s*=.*?"\s*\n/i);
				var originalPluralForms = match ? parseInt(match[1], 10) : null;
				if (originalPluralForms === null || originalPluralForms === fixRules.pluralForms) {
					process.stdout.write('not needed.\n');
					callback();
					return;
				}
				process.stdout.write(' (converting from ' + originalPluralForms + ' to ' + fixRules.pluralForms + ' plural forms)... ');
				po = po.replace(match[0], '\n"Plural-Forms: nplurals=' + fixRules.pluralForms + '; plural=' + fixRules.pluralFormula + ';\\n"\n');
				var fromLines = po.split('\n');
				var toLines = [];
				if (originalPluralForms > fixRules.pluralForms) {
					// we need to remove extra plural forms
					var keepLines = true;
					fromLines.forEach(function(line, lineIndex) {
						if (line.length > 0 && line.charAt(0) === '"') {
							if (keepLines) {
								toLines.push(line);
							}
							return;
						}
						keepLines = true;
						match = line.match(/^msgstr\[(\d+)\]/);
						if (match) {
							var pluralFormIndex = parseInt(match[1], 10);
							if (pluralFormIndex >= fixRules.pluralForms) {
								keepLines = false;
							}
						}
						if (keepLines) {
							toLines.push(line);
						}
					});
				} else {
					throw new Error('Increasing the plural forms count is not supported (not necessary at the time of writing this)');
				}
				po = toLines.join('\n');
				fs.writeFileSync(locale.poFile, po, 'utf8');
				process.stdout.write('done.\n');
				callback();
			}
		);
	}

	function compileLocale(locale, callback) {
		process.stdout.write('\tcompiling .mo file... ');
		execFile(
			'msgfmt', ['-o', locale.moFile, locale.poFile],
			{},
			function(error, stdout, stderr) {
				if(error !== null) {
					process.stderr.write(error.message || error);
					done(false);
					return;
				}
				process.stdout.write('done.\n');
				callback();
			}
		);
	}

	function parseLocale(allLocales, localeIndex, callback) {
		if(localeIndex >= allLocales.length) {
			callback();
			return;
		}
		var locale = allLocales[localeIndex];
		var destinationFolder;

		if (parameters.destination) {
			destinationFolder = path.resolve(__dirname, '..', parameters.destination);
		} else {
			destinationFolder = path.resolve(__dirname, config.DIR_BASE);
		}

		process.stdout.write('Locale ' + locale.name + '... ');
		get('/api/2/project/concrete5/resource/' + parameters.txResource + '/stats/' + locale.code + '/', {type: 'json'}, function(data) {
			var tot = data.translated_entities + data.untranslated_entities;
			locale.percentage = tot ? Math.round(data.translated_entities * 100 / tot) : 0;
			locale.passed = (locale.percentage >= txProgressLimit);
			process.stdout.write(' progress: ' + locale.percentage + '% -> ' + (locale.passed ? 'ok' : ('skipped (less than ' + txProgressLimit + '%)')) + ' \n');
			if(!locale.passed) {
				parseLocale(allLocales, localeIndex + 1, callback);
				return;
			}
			locale.poFile = path.join(destinationFolder, 'application/languages/' + locale.code + '/LC_MESSAGES/messages.po');
			locale.moFile = path.join(destinationFolder, 'application/languages/' + locale.code + '/LC_MESSAGES/messages.mo');
			downloadLocale(locale, function() {
				compileLocale(
					locale,
					function() {
						process.stdout.write('\tdeleting .po file... ');
						try {
							fs.unlinkSync(locale.poFile);
						}
						catch(e) {
							process.stderr.write(e.message || e);
							done(false);
							return;
						}
						process.stdout.write('done.\n');
						parseLocale(allLocales, localeIndex + 1, callback);
					}
				);
			});
		});
	}

	getAllLocales(function(allLocales) {
		parseLocale(allLocales, 0, function() {
			done(true);
		});
	});

};
<|endoftext|>


<commit_before>/**
 * Copyright 2018, the AMP HTML authors
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const NodeCache = require('node-cache');
const path = require('path');

const CACHE_CHECK_SECONDS = 60;
const CACHE_APPROVERS_KEY = 'approvers';
const CACHE_APPROVERS_TTL_SECONDS = 900;
const CACHE_TEAM_ID_KEY = 'team_id';
const CACHE_TEAM_ID_TTL_SECONDS = 86400;
const CACHE_TEAM_MEMBERS_KEY = 'team_members';
const CACHE_TEAM_MEMBERS_TTL_SECONDS = 3600;

/**
 * Get GitHub API parameters for bundle-size file actions.
 *
 * @param {string} filename the name of the file to act on.
 * @return {!object} GitHub API parameters to act on the file.
 */
function getBuildArtifactsFileParams_(filename) {
  return {
    owner: 'ampproject',
    repo: 'amphtml-build-artifacts',
    path: path.join('bundle-size', filename),
  };
}

/**
 * Utils for GitHub actions.
 */
class GitHubUtils {
  /**
   * @param {!Octokit} github an authenticated GitHub API object.
   * @param {!Logger} log logging function/object.
   * @param {NodeCache} cache an optional NodeCache instance.
   */
  constructor(github, log, cache) {
    this.github = github;
    this.log = log;
    this.cache =
      cache ||
      new NodeCache({
        'stdTTL': CACHE_APPROVERS_TTL_SECONDS,
        'checkperiod': CACHE_CHECK_SECONDS,
      });
  }

  /**
   * Get a file from the bundle-size directory in the AMPHTML build artifacts
   * repository.
   *
   * @param {string} filename the name of the file to retrieve.
   * @return {string} the text contents of the file.
   */
  async getBuildArtifactsFile(filename) {
    return await this.github.repos
      .getContents(getBuildArtifactsFileParams_(filename))
      .then(result => Buffer.from(result.data.content, 'base64').toString());
  }

  /**
   * Store a file in the bundle-size directory in the AMPHTML build artifacts
   * repository.
   *
   * @param {string} filename the name of the file to store into.
   * @param {string} contents text contents of the file.
   */
  async storeBuildArtifactsFile(filename, contents) {
    await this.github.repos.createOrUpdateFile({
      ...getBuildArtifactsFileParams_(filename),
      message: `bundle-size: ${filename}`,
      content: Buffer.from(contents).toString('base64'),
    });
  }

  /**
   * Get the mapping of compiled files to their approvers and thresholds.
   * @return {!Map<string, {approvers: !Array<string>, threshold: number}>}
   *   mapping of compiled files to the teams that can approve an increase, and
   *   the threshold in KB that requires an approval.
   */
  async getFileApprovalsMapping() {
    let approvers = this.cache.get(CACHE_APPROVERS_KEY);
    if (!approvers) {
      this.log(
        `Cache miss for ${CACHE_APPROVERS_KEY}. Fetching from GitHub...`
      );
      approvers = await this.github.repos
        .getContents({
          owner: 'ampproject',
          repo: 'amphtml',
          path: 'build-system/tasks/bundle-size/APPROVERS.json',
        })
        .then(result => Buffer.from(result.data.content, 'base64').toString())
        .then(JSON.parse);
      this.log(
        `Fetched ${CACHE_APPROVERS_KEY} from GitHub with ` +
          `${Object.keys(approvers).length} entries. Caching for ` +
          `${CACHE_APPROVERS_TTL_SECONDS} seconds.`
      );
      this.cache.set(
        CACHE_APPROVERS_KEY,
        approvers,
        CACHE_APPROVERS_TTL_SECONDS
      );
    }
    return approvers;
  }

  /**
   * Check whether the user is allowed to approve *all* bundle size changes.
   *
   * @param {string} username the username to check.
   * @return {boolean} true if the user is allowed to approve *all* bundle size
   *   changes.
   */
  async isSuperApprover(username) {
    return (
      await this.getTeamMembers(process.env.SUPER_USER_TEAMS.split(','))
    ).includes(username);
  }

  /**
   * Convert a team slug to a team id.
   *
   * @param {string} teamName the team slug (`organization/team`).
   * @return {number} the team id.
   */
  async getTeamId_(teamName) {
    const cacheKey = `${CACHE_TEAM_ID_KEY}/${teamName}`;
    const [org, teamSlug] = teamName.split('/');
    let teamId = this.cache.get(cacheKey);
    if (!teamId) {
      this.log(`Cache miss for ${cacheKey}. Fetching from GitHub...`);
      teamId = await this.github.teams
        .getByName({org, team_slug: teamSlug})
        .then(result => result.data.id);
      this.log(
        `Fetched team id ${teamId} for team ${teamName} from GitHub. Caching ` +
          `for ${CACHE_TEAM_ID_TTL_SECONDS} seconds.`
      );
      this.cache.set(cacheKey, teamId, CACHE_TEAM_ID_TTL_SECONDS);
    }
    return teamId;
  }

  /**
   * Get a list of all unique team members by team names.
   *
   * @param {!Array<string>} teamNames names of full team slug namesto get
   *   members of.
   * @return {!Array<string>} all members of the provided teams.
   */
  async getTeamMembers(teamNames) {
    const allTeamMembersPromises = teamNames.map(async teamName => {
      const teamId = await this.getTeamId_(teamName);
      const cacheKey = `${CACHE_TEAM_MEMBERS_KEY}/${teamId}`;
      let teamMembers = this.cache.get(cacheKey);
      if (!teamMembers) {
        this.log(`Cache miss for ${cacheKey}. Fetching from GitHub...`);
        teamMembers = await this.github.teams
          .listMembers({team_id: teamId})
          .then(result => result.data.map(user => user.login));
        this.log(
          `Fetched team members [${teamMembers.join(', ')}] for team id ` +
            `${teamId}. Caching for ${CACHE_TEAM_MEMBERS_TTL_SECONDS} seconds.`
        );
        this.cache.set(cacheKey, teamMembers, CACHE_TEAM_MEMBERS_TTL_SECONDS);
      }
      return teamMembers;
    }, this);
    const allTeamMembers = (await Promise.all(allTeamMembersPromises)).flat();
    return [...new Set(allTeamMembers)];
  }

  /**
   * Choose a random reviewer from list of potential approver teams.
   *
   * @param {!Array<string>} potentialReviewers list of GitHub usernames of all
   *   users who are members of the teams that can approve the bundle-size
   *   change of this pull request.
   * @return {string} the chosen reviewer username.
   */
  async getRandomReviewer_(potentialReviewers) {
    return potentialReviewers[
      Math.floor(Math.random() * potentialReviewers.length)
    ];
  }

  /**
   * Add an bundle size reviewer to the pull request.
   *
   * @param {!Octokit.PullsListReviewRequestsParams} pullRequest GitHub Pull
   *   Request params.
   * @param {!Array<string>} approverTeams list of all the teams whose members
   *   can approve the bundle-size change of this pull request.
   */
  async addBundleSizeReviewer(pullRequest, approverTeams) {
    const requestedReviewersResponse = await this.github.pullRequests.listReviewRequests(
      pullRequest
    );
    const reviewsResponse = await this.github.pullRequests.listReviews(
      pullRequest
    );
    const existingReviewers = new Set([
      ...requestedReviewersResponse.data.users.map(user => user.login),
      ...reviewsResponse.data.map(review => review.user.login),
    ]);

    const potentialReviewers = await this.getTeamMembers(approverTeams);
    if (!potentialReviewers.some(existingReviewers.has, existingReviewers)) {
      // None of the potential reviewers are in the PR's requested reviewers
      // list, so add a random reviewer here.
      const newReviewer = await this.getRandomReviewer_(potentialReviewers);
      this.log(
        `Chose reviewer ${newReviewer} from all of ` +
          `[${potentialReviewers.join(', ')}] for pull request ` +
          `${pullRequest.pull_number}`
      );
      try {
        // Choose a random capable username and add them as a reviewer to the pull
        // request.
        return await this.github.pullRequests.createReviewRequest({
          reviewers: [newReviewer],
          ...pullRequest,
        });
      } catch (error) {
        this.log.error(
          'ERROR: Failed to add a reviewer to pull request ' +
            `${pullRequest.pull_number}. Skipping...`
        );
        this.log.error(`Error message:\n`, error);
        throw error;
      }
    }
  }
}

module.exports = {GitHubUtils};
<commit_msg>Add log for when an approver is already a reviewer (#653)<commit_after>/**
 * Copyright 2018, the AMP HTML authors
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const NodeCache = require('node-cache');
const path = require('path');

const CACHE_CHECK_SECONDS = 60;
const CACHE_APPROVERS_KEY = 'approvers';
const CACHE_APPROVERS_TTL_SECONDS = 900;
const CACHE_TEAM_ID_KEY = 'team_id';
const CACHE_TEAM_ID_TTL_SECONDS = 86400;
const CACHE_TEAM_MEMBERS_KEY = 'team_members';
const CACHE_TEAM_MEMBERS_TTL_SECONDS = 3600;

/**
 * Get GitHub API parameters for bundle-size file actions.
 *
 * @param {string} filename the name of the file to act on.
 * @return {!object} GitHub API parameters to act on the file.
 */
function getBuildArtifactsFileParams_(filename) {
  return {
    owner: 'ampproject',
    repo: 'amphtml-build-artifacts',
    path: path.join('bundle-size', filename),
  };
}

/**
 * Utils for GitHub actions.
 */
class GitHubUtils {
  /**
   * @param {!Octokit} github an authenticated GitHub API object.
   * @param {!Logger} log logging function/object.
   * @param {NodeCache} cache an optional NodeCache instance.
   */
  constructor(github, log, cache) {
    this.github = github;
    this.log = log;
    this.cache =
      cache ||
      new NodeCache({
        'stdTTL': CACHE_APPROVERS_TTL_SECONDS,
        'checkperiod': CACHE_CHECK_SECONDS,
      });
  }

  /**
   * Get a file from the bundle-size directory in the AMPHTML build artifacts
   * repository.
   *
   * @param {string} filename the name of the file to retrieve.
   * @return {string} the text contents of the file.
   */
  async getBuildArtifactsFile(filename) {
    return await this.github.repos
      .getContents(getBuildArtifactsFileParams_(filename))
      .then(result => Buffer.from(result.data.content, 'base64').toString());
  }

  /**
   * Store a file in the bundle-size directory in the AMPHTML build artifacts
   * repository.
   *
   * @param {string} filename the name of the file to store into.
   * @param {string} contents text contents of the file.
   */
  async storeBuildArtifactsFile(filename, contents) {
    await this.github.repos.createOrUpdateFile({
      ...getBuildArtifactsFileParams_(filename),
      message: `bundle-size: ${filename}`,
      content: Buffer.from(contents).toString('base64'),
    });
  }

  /**
   * Get the mapping of compiled files to their approvers and thresholds.
   * @return {!Map<string, {approvers: !Array<string>, threshold: number}>}
   *   mapping of compiled files to the teams that can approve an increase, and
   *   the threshold in KB that requires an approval.
   */
  async getFileApprovalsMapping() {
    let approvers = this.cache.get(CACHE_APPROVERS_KEY);
    if (!approvers) {
      this.log(
        `Cache miss for ${CACHE_APPROVERS_KEY}. Fetching from GitHub...`
      );
      approvers = await this.github.repos
        .getContents({
          owner: 'ampproject',
          repo: 'amphtml',
          path: 'build-system/tasks/bundle-size/APPROVERS.json',
        })
        .then(result => Buffer.from(result.data.content, 'base64').toString())
        .then(JSON.parse);
      this.log(
        `Fetched ${CACHE_APPROVERS_KEY} from GitHub with ` +
          `${Object.keys(approvers).length} entries. Caching for ` +
          `${CACHE_APPROVERS_TTL_SECONDS} seconds.`
      );
      this.cache.set(
        CACHE_APPROVERS_KEY,
        approvers,
        CACHE_APPROVERS_TTL_SECONDS
      );
    }
    return approvers;
  }

  /**
   * Check whether the user is allowed to approve *all* bundle size changes.
   *
   * @param {string} username the username to check.
   * @return {boolean} true if the user is allowed to approve *all* bundle size
   *   changes.
   */
  async isSuperApprover(username) {
    return (
      await this.getTeamMembers(process.env.SUPER_USER_TEAMS.split(','))
    ).includes(username);
  }

  /**
   * Convert a team slug to a team id.
   *
   * @param {string} teamName the team slug (`organization/team`).
   * @return {number} the team id.
   */
  async getTeamId_(teamName) {
    const cacheKey = `${CACHE_TEAM_ID_KEY}/${teamName}`;
    const [org, teamSlug] = teamName.split('/');
    let teamId = this.cache.get(cacheKey);
    if (!teamId) {
      this.log(`Cache miss for ${cacheKey}. Fetching from GitHub...`);
      teamId = await this.github.teams
        .getByName({org, team_slug: teamSlug})
        .then(result => result.data.id);
      this.log(
        `Fetched team id ${teamId} for team ${teamName} from GitHub. Caching ` +
          `for ${CACHE_TEAM_ID_TTL_SECONDS} seconds.`
      );
      this.cache.set(cacheKey, teamId, CACHE_TEAM_ID_TTL_SECONDS);
    }
    return teamId;
  }

  /**
   * Get a list of all unique team members by team names.
   *
   * @param {!Array<string>} teamNames names of full team slug namesto get
   *   members of.
   * @return {!Array<string>} all members of the provided teams.
   */
  async getTeamMembers(teamNames) {
    const allTeamMembersPromises = teamNames.map(async teamName => {
      const teamId = await this.getTeamId_(teamName);
      const cacheKey = `${CACHE_TEAM_MEMBERS_KEY}/${teamId}`;
      let teamMembers = this.cache.get(cacheKey);
      if (!teamMembers) {
        this.log(`Cache miss for ${cacheKey}. Fetching from GitHub...`);
        teamMembers = await this.github.teams
          .listMembers({team_id: teamId})
          .then(result => result.data.map(user => user.login));
        this.log(
          `Fetched team members [${teamMembers.join(', ')}] for team id ` +
            `${teamId}. Caching for ${CACHE_TEAM_MEMBERS_TTL_SECONDS} seconds.`
        );
        this.cache.set(cacheKey, teamMembers, CACHE_TEAM_MEMBERS_TTL_SECONDS);
      }
      return teamMembers;
    }, this);
    const allTeamMembers = (await Promise.all(allTeamMembersPromises)).flat();
    return [...new Set(allTeamMembers)];
  }

  /**
   * Choose a random reviewer from list of potential approver teams.
   *
   * @param {!Array<string>} potentialReviewers list of GitHub usernames of all
   *   users who are members of the teams that can approve the bundle-size
   *   change of this pull request.
   * @return {string} the chosen reviewer username.
   */
  async getRandomReviewer_(potentialReviewers) {
    return potentialReviewers[
      Math.floor(Math.random() * potentialReviewers.length)
    ];
  }

  /**
   * Add an bundle size reviewer to the pull request.
   *
   * @param {!Octokit.PullsListReviewRequestsParams} pullRequest GitHub Pull
   *   Request params.
   * @param {!Array<string>} approverTeams list of all the teams whose members
   *   can approve the bundle-size change of this pull request.
   */
  async addBundleSizeReviewer(pullRequest, approverTeams) {
    const requestedReviewersResponse = await this.github.pullRequests.listReviewRequests(
      pullRequest
    );
    const reviewsResponse = await this.github.pullRequests.listReviews(
      pullRequest
    );
    const existingReviewers = new Set([
      ...requestedReviewersResponse.data.users.map(user => user.login),
      ...reviewsResponse.data.map(review => review.user.login),
    ]);

    const potentialReviewers = await this.getTeamMembers(approverTeams);
    if (potentialReviewers.some(existingReviewers.has, existingReviewers)) {
      this.log(
        `Reviewers set [${Array.from(existingReviewers).join(', ')}] of pull ` +
          `request ${pullRequest.pull_number} already contains an approver ` +
          `from potential approvers set [${potentialReviewers.join(', ')}]`
      );
      return;
    }

    const newReviewer = await this.getRandomReviewer_(potentialReviewers);
    this.log(
      `Chose reviewer ${newReviewer} from all of ` +
        `[${potentialReviewers.join(', ')}] for pull request ` +
        `${pullRequest.pull_number}`
    );
    try {
      // Choose a random capable username and add them as a reviewer to the pull
      // request.
      return await this.github.pullRequests.createReviewRequest({
        reviewers: [newReviewer],
        ...pullRequest,
      });
    } catch (error) {
      this.log.error(
        'ERROR: Failed to add a reviewer to pull request ' +
          `${pullRequest.pull_number}. Skipping...`
      );
      this.log.error(`Error message:\n`, error);
      throw error;
    }
  }
}

module.exports = {GitHubUtils};
<|endoftext|>


<commit_before>/* @flow */

import config from '../config'
import VNode, { emptyVNode } from '../vdom/vnode'
import { normalizeChildren } from '../vdom/helpers'
import {
  warn, formatComponentName, bind, isObject, toObject,
  nextTick, resolveAsset, _toString, toNumber
} from '../util/index'

import { createElement } from '../vdom/create-element'

export const renderState: {
  activeInstance: ?Component
} = {
  activeInstance: null
}

export function initRender (vm: Component) {
  vm.$vnode = null // the placeholder node in parent tree
  vm._vnode = null // the root of the child tree
  vm._staticTrees = null
  vm.$slots = {}
  // bind the public createElement fn to this instance
  // so that we get proper render context inside it.
  vm.$createElement = bind(createElement, vm)
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

export function renderMixin (Vue: Class<Component>) {
  Vue.prototype.$nextTick = function (fn: Function) {
    nextTick(fn, this)
  }

  Vue.prototype._render = function (): VNode {
    const vm: Component = this

    // set current active instance
    const prev = renderState.activeInstance
    renderState.activeInstance = vm

    const {
      render,
      staticRenderFns,
      _renderChildren,
      _parentVnode
    } = vm.$options

    if (staticRenderFns && !vm._staticTrees) {
      vm._staticTrees = []
    }
    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode
    // resolve slots. becaues slots are rendered in parent scope,
    // we set the activeInstance to parent.
    vm.$slots = resolveSlots(_renderChildren)
    // render self
    let vnode
    try {
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      if (process.env.NODE_ENV !== 'production') {
        warn(`Error when rendering ${formatComponentName(vm)}:`)
      }
      /* istanbul ignore else */
      if (config.errorHandler) {
        config.errorHandler.call(null, e, vm)
      } else {
        if (config._isServer) {
          throw e
        } else {
          setTimeout(() => { throw e }, 0)
        }
      }
      // return previous vnode to prevent render error causing blank component
      vnode = vm._vnode
    }
    // return empty vnode in case the render function errored out
    if (!(vnode instanceof VNode)) {
      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
        warn(
          'Multiple root nodes returned from render function. Render function ' +
          'should return a single root node.',
          vm
        )
      }
      vnode = emptyVNode()
    }
    // set parent
    vnode.parent = _parentVnode
    // restore render state
    renderState.activeInstance = prev
    return vnode
  }

  // shorthands used in render functions
  Vue.prototype._h = createElement
  // toString for mustaches
  Vue.prototype._s = _toString
  // number conversion
  Vue.prototype._n = toNumber

  //
  Vue.prototype._m = function renderStatic (index?: number): Object | void {
    return this._staticTrees[index] || (
      this._staticTrees[index] = this.$options.staticRenderFns[index].call(
        this._renderProxy
      )
    )
  }

  // filter resolution helper
  const identity = _ => _
  Vue.prototype._f = function resolveFilter (id) {
    return resolveAsset(this.$options, 'filters', id, true) || identity
  }

  // render v-for
  Vue.prototype._l = function renderList (
    val: any,
    render: () => VNode
  ): ?Array<VNode> {
    let ret: ?Array<VNode>, i, l, keys, key
    if (Array.isArray(val)) {
      ret = new Array(val.length)
      for (i = 0, l = val.length; i < l; i++) {
        ret[i] = render(val[i], i)
      }
    } else if (typeof val === 'number') {
      ret = new Array(val)
      for (i = 0; i < val; i++) {
        ret[i] = render(i + 1, i)
      }
    } else if (isObject(val)) {
      keys = Object.keys(val)
      ret = new Array(keys.length)
      for (i = 0, l = keys.length; i < l; i++) {
        key = keys[i]
        ret[i] = render(val[key], key, i)
      }
    }
    return ret
  }

  // apply v-bind object
  Vue.prototype._b = function bindProps (
    vnode: VNodeWithData,
    value: any,
    asProp?: boolean) {
    if (value) {
      if (!isObject(value)) {
        process.env.NODE_ENV !== 'production' && warn(
          'v-bind without argument expects an Object or Array value',
          this
        )
      } else {
        if (Array.isArray(value)) {
          value = toObject(value)
        }
        const data = vnode.data
        for (const key in value) {
          const hash = asProp || config.mustUseProp(key)
            ? data.domProps || (data.domProps = {})
            : data.attrs || (data.attrs = {})
          hash[key] = value[key]
        }
      }
    }
  }

  // expose v-on keyCodes
  Vue.prototype._k = function getKeyCodes (key: string): any {
    return config.keyCodes[key]
  }
}

export function resolveSlots (
  renderChildren?: Array<any> | () => Array<any> | string
): Object {
  const slots = {}
  if (!renderChildren) {
    return slots
  }
  const children = normalizeChildren(renderChildren) || []
  const defaultSlot = []
  let name, child
  for (let i = 0, l = children.length; i < l; i++) {
    child = children[i]
    if (child.data && (name = child.data.slot)) {
      delete child.data.slot
      const slot = (slots[name] || (slots[name] = []))
      if (child.tag === 'template') {
        slot.push.apply(slot, child.children)
      } else {
        slot.push(child)
      }
    } else {
      defaultSlot.push(child)
    }
  }
  // ignore single whitespace
  if (defaultSlot.length && !(
    defaultSlot.length === 1 &&
    defaultSlot[0].text === ' '
  )) {
    slots.default = defaultSlot
  }
  return slots
}
<commit_msg>fix flow<commit_after>/* @flow */

import config from '../config'
import VNode, { emptyVNode } from '../vdom/vnode'
import { normalizeChildren } from '../vdom/helpers'
import {
  warn, formatComponentName, bind, isObject, toObject,
  nextTick, resolveAsset, _toString, toNumber
} from '../util/index'

import { createElement } from '../vdom/create-element'

export const renderState: {
  activeInstance: ?Component
} = {
  activeInstance: null
}

export function initRender (vm: Component) {
  vm.$vnode = null // the placeholder node in parent tree
  vm._vnode = null // the root of the child tree
  vm._staticTrees = null
  vm.$slots = {}
  // bind the public createElement fn to this instance
  // so that we get proper render context inside it.
  vm.$createElement = bind(createElement, vm)
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

export function renderMixin (Vue: Class<Component>) {
  Vue.prototype.$nextTick = function (fn: Function) {
    nextTick(fn, this)
  }

  Vue.prototype._render = function (): VNode {
    const vm: Component = this

    // set current active instance
    const prev = renderState.activeInstance
    renderState.activeInstance = vm

    const {
      render,
      staticRenderFns,
      _renderChildren,
      _parentVnode
    } = vm.$options

    if (staticRenderFns && !vm._staticTrees) {
      vm._staticTrees = []
    }
    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode
    // resolve slots. becaues slots are rendered in parent scope,
    // we set the activeInstance to parent.
    vm.$slots = resolveSlots(_renderChildren)
    // render self
    let vnode
    try {
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      if (process.env.NODE_ENV !== 'production') {
        warn(`Error when rendering ${formatComponentName(vm)}:`)
      }
      /* istanbul ignore else */
      if (config.errorHandler) {
        config.errorHandler.call(null, e, vm)
      } else {
        if (config._isServer) {
          throw e
        } else {
          setTimeout(() => { throw e }, 0)
        }
      }
      // return previous vnode to prevent render error causing blank component
      vnode = vm._vnode
    }
    // return empty vnode in case the render function errored out
    if (!(vnode instanceof VNode)) {
      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
        warn(
          'Multiple root nodes returned from render function. Render function ' +
          'should return a single root node.',
          vm
        )
      }
      vnode = emptyVNode()
    }
    // set parent
    vnode.parent = _parentVnode
    // restore render state
    renderState.activeInstance = prev
    return vnode
  }

  // shorthands used in render functions
  Vue.prototype._h = createElement
  // toString for mustaches
  Vue.prototype._s = _toString
  // number conversion
  Vue.prototype._n = toNumber

  //
  Vue.prototype._m = function renderStatic (index?: number): Object | void {
    return this._staticTrees[index] || (
      this._staticTrees[index] = this.$options.staticRenderFns[index].call(
        this._renderProxy
      )
    )
  }

  // filter resolution helper
  const identity = _ => _
  Vue.prototype._f = function resolveFilter (id) {
    return resolveAsset(this.$options, 'filters', id, true) || identity
  }

  // render v-for
  Vue.prototype._l = function renderList (
    val: any,
    render: () => VNode
  ): ?Array<VNode> {
    let ret: ?Array<VNode>, i, l, keys, key
    if (Array.isArray(val)) {
      ret = new Array(val.length)
      for (i = 0, l = val.length; i < l; i++) {
        ret[i] = render(val[i], i)
      }
    } else if (typeof val === 'number') {
      ret = new Array(val)
      for (i = 0; i < val; i++) {
        ret[i] = render(i + 1, i)
      }
    } else if (isObject(val)) {
      keys = Object.keys(val)
      ret = new Array(keys.length)
      for (i = 0, l = keys.length; i < l; i++) {
        key = keys[i]
        ret[i] = render(val[key], key, i)
      }
    }
    return ret
  }

  // apply v-bind object
  Vue.prototype._b = function bindProps (
    vnode: VNodeWithData,
    value: any,
    asProp?: boolean) {
    if (value) {
      if (!isObject(value)) {
        process.env.NODE_ENV !== 'production' && warn(
          'v-bind without argument expects an Object or Array value',
          this
        )
      } else {
        if (Array.isArray(value)) {
          value = toObject(value)
        }
        const data = vnode.data
        for (const key in value) {
          const hash = asProp || config.mustUseProp(key)
            ? data.domProps || (data.domProps = {})
            : data.attrs || (data.attrs = {})
          hash[key] = value[key]
        }
      }
    }
  }

  // expose v-on keyCodes
  Vue.prototype._k = function getKeyCodes (key: string): any {
    return config.keyCodes[key]
  }
}

export function resolveSlots (renderChildren: any): Object {
  const slots = {}
  if (!renderChildren) {
    return slots
  }
  const children = normalizeChildren(renderChildren) || []
  const defaultSlot = []
  let name, child
  for (let i = 0, l = children.length; i < l; i++) {
    child = children[i]
    if (child.data && (name = child.data.slot)) {
      delete child.data.slot
      const slot = (slots[name] || (slots[name] = []))
      if (child.tag === 'template') {
        slot.push.apply(slot, child.children)
      } else {
        slot.push(child)
      }
    } else {
      defaultSlot.push(child)
    }
  }
  // ignore single whitespace
  if (defaultSlot.length && !(
    defaultSlot.length === 1 &&
    defaultSlot[0].text === ' '
  )) {
    slots.default = defaultSlot
  }
  return slots
}
<|endoftext|>


<commit_before>import { getDocument } from 'ssr-window';
import $ from '../../shared/dom.js';

export default function loopCreate() {
  const swiper = this;
  const document = getDocument();
  const { params, $wrapperEl } = swiper;
  // Remove duplicated slides
  $wrapperEl.children(`.${params.slideClass}.${params.slideDuplicateClass}`).remove();

  let slides = $wrapperEl.children(`.${params.slideClass}`);

  if (params.loopFillGroupWithBlank) {
    const blankSlidesNum = params.slidesPerGroup - (slides.length % params.slidesPerGroup);
    if (blankSlidesNum !== params.slidesPerGroup) {
      for (let i = 0; i < blankSlidesNum; i += 1) {
        const blankNode = $(document.createElement('div')).addClass(
          `${params.slideClass} ${params.slideBlankClass}`,
        );
        $wrapperEl.append(blankNode);
      }
      slides = $wrapperEl.children(`.${params.slideClass}`);
    }
  }

  if (params.slidesPerView === 'auto' && !params.loopedSlides) params.loopedSlides = slides.length;

  swiper.loopedSlides = Math.ceil(parseFloat(params.loopedSlides || params.slidesPerView, 10));
  swiper.loopedSlides += params.loopAdditionalSlides;
  if (swiper.loopedSlides > slides.length) {
    swiper.loopedSlides = slides.length;
  }

  const prependSlides = [];
  const appendSlides = [];
  slides.each((el, index) => {
    const slide = $(el);
    if (index < swiper.loopedSlides) {
      appendSlides.push(el);
    }
    if (index < slides.length && index >= slides.length - swiper.loopedSlides) {
      prependSlides.push(el);
    }
    slide.attr('data-swiper-slide-index', index);
  });
  for (let i = 0; i < appendSlides.length; i += 1) {
    $wrapperEl.append($(appendSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass));
  }
  for (let i = prependSlides.length - 1; i >= 0; i -= 1) {
    $wrapperEl.prepend($(prependSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass));
  }
}
<commit_msg>feat(core): add support to loop with slotted elements (#5117)<commit_after>import { getDocument } from 'ssr-window';
import $ from '../../shared/dom.js';

export default function loopCreate() {
  const swiper = this;
  const document = getDocument();
  const { params, $wrapperEl } = swiper;
  // Remove duplicated slides
  const $selector = $($wrapperEl.children()[0].parentNode);
  $selector.children(`.${params.slideClass}.${params.slideDuplicateClass}`).remove();

  let slides = $selector.children(`.${params.slideClass}`);

  if (params.loopFillGroupWithBlank) {
    const blankSlidesNum = params.slidesPerGroup - (slides.length % params.slidesPerGroup);
    if (blankSlidesNum !== params.slidesPerGroup) {
      for (let i = 0; i < blankSlidesNum; i += 1) {
        const blankNode = $(document.createElement('div')).addClass(
          `${params.slideClass} ${params.slideBlankClass}`,
        );
        $selector.append(blankNode);
      }
      slides = $selector.children(`.${params.slideClass}`);
    }
  }

  if (params.slidesPerView === 'auto' && !params.loopedSlides) params.loopedSlides = slides.length;

  swiper.loopedSlides = Math.ceil(parseFloat(params.loopedSlides || params.slidesPerView, 10));
  swiper.loopedSlides += params.loopAdditionalSlides;
  if (swiper.loopedSlides > slides.length) {
    swiper.loopedSlides = slides.length;
  }

  const prependSlides = [];
  const appendSlides = [];
  slides.each((el, index) => {
    const slide = $(el);
    if (index < swiper.loopedSlides) {
      appendSlides.push(el);
    }
    if (index < slides.length && index >= slides.length - swiper.loopedSlides) {
      prependSlides.push(el);
    }
    slide.attr('data-swiper-slide-index', index);
  });
  for (let i = 0; i < appendSlides.length; i += 1) {
    $selector.append($(appendSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass));
  }
  for (let i = prependSlides.length - 1; i >= 0; i -= 1) {
    $selector.prepend($(prependSlides[i].cloneNode(true)).addClass(params.slideDuplicateClass));
  }
}
<|endoftext|>


<commit_before>
/* =========================================================================
 * Svelto - Core - Pointer
 * =========================================================================
 * Copyright (c) 2015-2016 Fabio Spampinato
 * Licensed under MIT (https://github.com/svelto/svelto/blob/master/LICENSE)
 * =========================================================================
 * @require core/browser/browser.js
 * @require core/mouse/mouse.js
 * @require core/svelto/svelto.js
 * ========================================================================= */

// Basically it exists other than to provide the convinient `Pointer` global also for removing the 300ms delay on click by providing the `tap` event

(function ( $, _, Svelto, Browser, Mouse ) {

  'use strict';

  /* VARIABLES */

  let prefix = 'spointer';

  /* POINTER */

  let Pointer = {
    /* OPTIONS */
    options: {
      events: {
        prefix: prefix,
        emulated: {
          timeout: 300 // Amount of milliseconds to wait for an emulated event
        }
      },
      tap: {
        threshold: 6 // Over this distance threshold the touch event won't be considered a tap
      },
      dbltap: {
        interval: 300 // 2 taps within this interval will trigger a dbltap event
      },
    },
    /* EVENTS */
    tap: `${prefix}tap`,
    dbltap: `${prefix}dbltap`,
    click: 'click',
    dblclick: 'dblclick',
    down: Browser.is.touchDevice ? 'touchstart mousedown' : 'mousedown',
    move: Browser.is.touchDevice ? 'touchmove mousemove' : 'mousemove',
    up: Browser.is.touchDevice ? 'touchend mouseup' : 'mouseup',
    cancel: Browser.is.touchDevice ? 'touchcancel mouseleave' : 'mouseleave',
    over: 'mouseover',
    enter: 'mouseenter',
    out: 'mouseout',
    leave: 'mouseleave',
    /* METHODS */
    isDeviceEvent ( event, device ) {
      return _.startsWith ( event.type, device.toLowerCase () );
    },
    isPointerEvent ( event ) {
      return Pointer.isDeviceEvent ( event, Pointer.options.events.prefix );
    },
    isMouseEvent ( event ) {
      return Pointer.isDeviceEvent ( event, 'mouse' );
    },
    isTouchEvent ( event ) {
      return Pointer.isDeviceEvent ( event, 'touch' );
    }
  };

  /* EVENTS METHODS */

  ['tap', 'dbltap'].forEach ( name => {

    $.fn[name] = function ( data, fn ) {

      return arguments.length ? this.on ( Pointer[name], null, data, fn ) : this.triggger ( name );

    };

  });

  /* ----- POINTER LOGIC ----- */

  /* VARIABLES */

  let $document = $(document),
      canTouch = Browser.is.touchDevice,
      isTouch,
      delta = 0,
      skipping,
      scrolled,
      timeoutId,
      downEvent,
      prevTapTimestamp = 0,
      dbltapTriggerable = true;

  /* EVENT CREATOR */

  function createEvent ( name, originalEvent ) {

    let event = $.Event ( name );

    event.originalEvent = originalEvent;

    return event;

  }

  /* HANDLERS */

  function downHandler ( event ) {

    if ( canTouch ) {

      isTouch = Pointer.isTouchEvent ( event );

      if ( isTouch ) {

        scrolled = false;

        window.onscroll = scrollHandler;

        delta++;

      } else if ( delta > 0 ) {

        skipping = true;

        delta--;

        return;

      }

      skipping = false;

    }

    downEvent = event;

  }

  function upHandler ( event ) {

    if ( skipping ) return;
    if ( !downEvent ) return;

    reset ();

    if ( isTouch && scrolled ) return;
    if ( !isTouch && !Mouse.hasButton ( event, Mouse.buttons.LEFT, true ) ) return;
    if ( downEvent.target !== event.target ) return;

    if ( isTouch ) {

      let downXY = $.eventXY ( downEvent ),
          upXY = $.eventXY ( event ),
          threshold = Pointer.options.tap.threshold;

      if ( Math.abs ( downXY.x - upXY.x ) > threshold || Math.abs ( downXY.y - upXY.y ) > threshold ) return;

    }

    let tapTimestamp = event.timeStamp || Date.now (),
        $target = $(downEvent.target);

    $target.trigger ( createEvent ( Pointer.tap, event ) );

    if ( tapTimestamp - prevTapTimestamp <= Pointer.options.dbltap.interval ) {

      if ( dbltapTriggerable ) {

        $target.trigger ( createEvent ( Pointer.dbltap, event ) );

        dbltapTriggerable = false;

      }

    } else {

      dbltapTriggerable = true;

    }

    prevTapTimestamp = tapTimestamp;

  }

  function scrollHandler () {

    scrolled = true;

    window.onscroll = null;

  }

  /* RESET */

  function reset () {

    setTimeout ( resetEvents, 0 );

    if ( isTouch ) {

      if ( !scrolled ) window.onscroll = null;

      if ( timeoutId ) clearTimeout ( timeoutId );

      timeoutId = setTimeout ( resetDelta, Pointer.options.events.emulated.timeout );

    }

  }

  function resetEvents () {

    downEvent = undefined;

  }

  function resetDelta () {

    delta = 0;

    timeoutId = false;

  }

  /* INIT */

  $document.on ( Pointer.down, downHandler );
  $document.on ( Pointer.up, upHandler );
  $document.on ( Pointer.cancel, reset );

  /* EXPORT */

  Svelto.Pointer = Pointer;

}( Svelto.$, Svelto._, Svelto, Svelto.Browser, Svelto.Mouse ));
<commit_msg>Pointer: increased emulated timeout to 500ms<commit_after>
/* =========================================================================
 * Svelto - Core - Pointer
 * =========================================================================
 * Copyright (c) 2015-2016 Fabio Spampinato
 * Licensed under MIT (https://github.com/svelto/svelto/blob/master/LICENSE)
 * =========================================================================
 * @require core/browser/browser.js
 * @require core/mouse/mouse.js
 * @require core/svelto/svelto.js
 * ========================================================================= */

// Basically it exists other than to provide the convinient `Pointer` global also for removing the 300ms delay on click by providing the `tap` event

(function ( $, _, Svelto, Browser, Mouse ) {

  'use strict';

  /* VARIABLES */

  let prefix = 'spointer';

  /* POINTER */

  let Pointer = {
    /* OPTIONS */
    options: {
      events: {
        prefix: prefix,
        emulated: {
          timeout: 500 // Milliseconds to wait for an emulated event
        }
      },
      tap: {
        threshold: 6 // Over this distance threshold the touch event won't be considered a tap
      },
      dbltap: {
        interval: 300 // 2 taps within this interval will trigger a dbltap event
      },
    },
    /* EVENTS */
    tap: `${prefix}tap`,
    dbltap: `${prefix}dbltap`,
    click: 'click',
    dblclick: 'dblclick',
    down: Browser.is.touchDevice ? 'touchstart mousedown' : 'mousedown',
    move: Browser.is.touchDevice ? 'touchmove mousemove' : 'mousemove',
    up: Browser.is.touchDevice ? 'touchend mouseup' : 'mouseup',
    cancel: Browser.is.touchDevice ? 'touchcancel mouseleave' : 'mouseleave',
    over: 'mouseover',
    enter: 'mouseenter',
    out: 'mouseout',
    leave: 'mouseleave',
    /* METHODS */
    isDeviceEvent ( event, device ) {
      return _.startsWith ( event.type, device.toLowerCase () );
    },
    isPointerEvent ( event ) {
      return Pointer.isDeviceEvent ( event, Pointer.options.events.prefix );
    },
    isMouseEvent ( event ) {
      return Pointer.isDeviceEvent ( event, 'mouse' );
    },
    isTouchEvent ( event ) {
      return Pointer.isDeviceEvent ( event, 'touch' );
    }
  };

  /* EVENTS METHODS */

  ['tap', 'dbltap'].forEach ( name => {

    $.fn[name] = function ( data, fn ) {

      return arguments.length ? this.on ( Pointer[name], null, data, fn ) : this.triggger ( name );

    };

  });

  /* ----- POINTER LOGIC ----- */

  /* VARIABLES */

  let $document = $(document),
      canTouch = Browser.is.touchDevice,
      isTouch,
      delta = 0,
      skipping,
      scrolled,
      timeoutId,
      downEvent,
      prevTapTimestamp = 0,
      dbltapTriggerable = true;

  /* EVENT CREATOR */

  function createEvent ( name, originalEvent ) {

    let event = $.Event ( name );

    event.originalEvent = originalEvent;

    return event;

  }

  /* HANDLERS */

  function downHandler ( event ) {

    if ( canTouch ) {

      isTouch = Pointer.isTouchEvent ( event );

      if ( isTouch ) {

        scrolled = false;

        window.onscroll = scrollHandler;

        delta++;

      } else if ( delta > 0 ) {

        skipping = true;

        delta--;

        return;

      }

      skipping = false;

    }

    downEvent = event;

  }

  function upHandler ( event ) {

    if ( skipping ) return;
    if ( !downEvent ) return;

    reset ();

    if ( isTouch && scrolled ) return;
    if ( !isTouch && !Mouse.hasButton ( event, Mouse.buttons.LEFT, true ) ) return;
    if ( downEvent.target !== event.target ) return;

    if ( isTouch ) {

      let downXY = $.eventXY ( downEvent ),
          upXY = $.eventXY ( event ),
          threshold = Pointer.options.tap.threshold;

      if ( Math.abs ( downXY.x - upXY.x ) > threshold || Math.abs ( downXY.y - upXY.y ) > threshold ) return;

    }

    let tapTimestamp = event.timeStamp || Date.now (),
        $target = $(downEvent.target);

    $target.trigger ( createEvent ( Pointer.tap, event ) );

    if ( tapTimestamp - prevTapTimestamp <= Pointer.options.dbltap.interval ) {

      if ( dbltapTriggerable ) {

        $target.trigger ( createEvent ( Pointer.dbltap, event ) );

        dbltapTriggerable = false;

      }

    } else {

      dbltapTriggerable = true;

    }

    prevTapTimestamp = tapTimestamp;

  }

  function scrollHandler () {

    scrolled = true;

    window.onscroll = null;

  }

  /* RESET */

  function reset () {

    setTimeout ( resetEvents, 0 );

    if ( isTouch ) {

      if ( !scrolled ) window.onscroll = null;

      if ( timeoutId ) clearTimeout ( timeoutId );

      timeoutId = setTimeout ( resetDelta, Pointer.options.events.emulated.timeout );

    }

  }

  function resetEvents () {

    downEvent = undefined;

  }

  function resetDelta () {

    delta = 0;

    timeoutId = false;

  }

  /* INIT */

  $document.on ( Pointer.down, downHandler );
  $document.on ( Pointer.up, upHandler );
  $document.on ( Pointer.cancel, reset );

  /* EXPORT */

  Svelto.Pointer = Pointer;

}( Svelto.$, Svelto._, Svelto, Svelto.Browser, Svelto.Mouse ));
<|endoftext|>


<commit_before>import {
  isFn,
  isStr,
  hasOwn,
  isPlainObject
} from 'uni-shared'

import {
  isSyncApi,
  isContextApi
} from '../helpers/promise'

import { protocols } from 'uni-platform/runtime/api/protocols'

const CALLBACKS = ['success', 'fail', 'cancel', 'complete']

function processCallback (methodName, method, returnValue) {
  return function (res) {
    return method(processReturnValue(methodName, res, returnValue))
  }
}

function processArgs (methodName, fromArgs, argsOption = {}, returnValue = {}, keepFromArgs = false) {
  if (isPlainObject(fromArgs)) { // 一般 api 的参数解析
    const toArgs = keepFromArgs === true ? fromArgs : {} // returnValue 为 false 时，说明是格式化返回值，直接在返回值对象上修改赋值
    if (isFn(argsOption)) {
      argsOption = argsOption(fromArgs, toArgs) || {}
    }
    for (const key in fromArgs) {
      if (hasOwn(argsOption, key)) {
        let keyOption = argsOption[key]
        if (isFn(keyOption)) {
          keyOption = keyOption(fromArgs[key], fromArgs, toArgs)
        }
        if (!keyOption) { // 不支持的参数
          console.warn(`__PLATFORM_TITLE__ ${methodName}暂不支持${key}`)
        } else if (isStr(keyOption)) { // 重写参数 key
          toArgs[keyOption] = fromArgs[key]
        } else if (isPlainObject(keyOption)) { // {name:newName,value:value}可重新指定参数 key:value
          toArgs[keyOption.name ? keyOption.name : key] = keyOption.value
        }
      } else if (CALLBACKS.indexOf(key) !== -1) {
        toArgs[key] = processCallback(methodName, fromArgs[key], returnValue)
      } else {
        if (!keepFromArgs) {
          toArgs[key] = fromArgs[key]
        }
      }
    }
    return toArgs
  } else if (isFn(fromArgs)) {
    fromArgs = processCallback(methodName, fromArgs, returnValue)
  }
  return fromArgs
}

function processReturnValue (methodName, res, returnValue, keepReturnValue = false) {
  if (isFn(protocols.returnValue)) { // 处理通用 returnValue
    res = protocols.returnValue(methodName, res)
  }
  return processArgs(methodName, res, returnValue, {}, keepReturnValue)
}

export default function wrapper (methodName, method) {
  if (hasOwn(protocols, methodName)) {
    const protocol = protocols[methodName]
    if (!protocol) { // 暂不支持的 api
      return function () {
        console.error(`__PLATFORM_TITLE__ 暂不支持${methodName}`)
      }
    }
    return function (arg1, arg2) { // 目前 api 最多两个参数
      let options = protocol
      if (isFn(protocol)) {
        options = protocol(arg1)
      }

      arg1 = processArgs(methodName, arg1, options.args, options.returnValue)

      const args = [arg1]
      if (typeof arg2 !== 'undefined') {
        args.push(arg2)
      }
      const returnValue = __GLOBAL__[options.name || methodName].apply(__GLOBAL__, args)
      if (isSyncApi(methodName)) { // 同步 api
        return processReturnValue(methodName, returnValue, options.returnValue, isContextApi(methodName))
      }
      return returnValue
    }
  }
  return method
}
<commit_msg>fix: 修复传入非function的callback时报错的Bug<commit_after>import {
  isFn,
  isStr,
  hasOwn,
  isPlainObject
} from 'uni-shared'

import {
  isSyncApi,
  isContextApi
} from '../helpers/promise'

import { protocols } from 'uni-platform/runtime/api/protocols'

const CALLBACKS = ['success', 'fail', 'cancel', 'complete']

function processCallback (methodName, method, returnValue) {
  return function (res) {
    return method(processReturnValue(methodName, res, returnValue))
  }
}

function processArgs (methodName, fromArgs, argsOption = {}, returnValue = {}, keepFromArgs = false) {
  if (isPlainObject(fromArgs)) { // 一般 api 的参数解析
    const toArgs = keepFromArgs === true ? fromArgs : {} // returnValue 为 false 时，说明是格式化返回值，直接在返回值对象上修改赋值
    if (isFn(argsOption)) {
      argsOption = argsOption(fromArgs, toArgs) || {}
    }
    for (const key in fromArgs) {
      if (hasOwn(argsOption, key)) {
        let keyOption = argsOption[key]
        if (isFn(keyOption)) {
          keyOption = keyOption(fromArgs[key], fromArgs, toArgs)
        }
        if (!keyOption) { // 不支持的参数
          console.warn(`__PLATFORM_TITLE__ ${methodName}暂不支持${key}`)
        } else if (isStr(keyOption)) { // 重写参数 key
          toArgs[keyOption] = fromArgs[key]
        } else if (isPlainObject(keyOption)) { // {name:newName,value:value}可重新指定参数 key:value
          toArgs[keyOption.name ? keyOption.name : key] = keyOption.value
        }
      } else if (CALLBACKS.indexOf(key) !== -1) {
        if (isFn(fromArgs[key])) {
          toArgs[key] = processCallback(methodName, fromArgs[key], returnValue)
        }
      } else {
        if (!keepFromArgs) {
          toArgs[key] = fromArgs[key]
        }
      }
    }
    return toArgs
  } else if (isFn(fromArgs)) {
    fromArgs = processCallback(methodName, fromArgs, returnValue)
  }
  return fromArgs
}

function processReturnValue (methodName, res, returnValue, keepReturnValue = false) {
  if (isFn(protocols.returnValue)) { // 处理通用 returnValue
    res = protocols.returnValue(methodName, res)
  }
  return processArgs(methodName, res, returnValue, {}, keepReturnValue)
}

export default function wrapper (methodName, method) {
  if (hasOwn(protocols, methodName)) {
    const protocol = protocols[methodName]
    if (!protocol) { // 暂不支持的 api
      return function () {
        console.error(`__PLATFORM_TITLE__ 暂不支持${methodName}`)
      }
    }
    return function (arg1, arg2) { // 目前 api 最多两个参数
      let options = protocol
      if (isFn(protocol)) {
        options = protocol(arg1)
      }

      arg1 = processArgs(methodName, arg1, options.args, options.returnValue)

      const args = [arg1]
      if (typeof arg2 !== 'undefined') {
        args.push(arg2)
      }
      const returnValue = __GLOBAL__[options.name || methodName].apply(__GLOBAL__, args)
      if (isSyncApi(methodName)) { // 同步 api
        return processReturnValue(methodName, returnValue, options.returnValue, isContextApi(methodName))
      }
      return returnValue
    }
  }
  return method
}
<|endoftext|>


<commit_before>var path = require('path');
var package = require('./package');
var webpack = require('webpack');
var inspect = require('util').inspect;
var Busboy = require('busboy');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackConfig = require('./webpack.config');
var webpackCompiler = webpack(webpackConfig);
var handler;

// {{ settings for nico
exports.site = {
  name: package.title,
  description: package.description,
  repo: package.repository.url,
  issues: package.bugs.url
};
exports.package = package;
exports.theme = 'site';
exports.source = process.cwd();
exports.output = path.join(process.cwd(), '_site');
exports.permalink = '{{directory}}/{{filename}}';
exports.ignorefilter = function(filepath, subdir) {
  var extname = path.extname(filepath);
  if (extname === '.tmp' || extname === '.bak') {
    return false;
  }
  if (/\.DS_Store/.test(filepath)) {
    return false;
  }
  if (/^(_site|_theme|node_modules|site|\.idea)/.test(subdir)) {
    return false;
  }
  return true;
};
exports.middlewares = [
  {
    name: 'upload',
    filter: /upload\.do?$/,
    handle: function(req, res, next) {
      if (req.method === 'POST') {
        var busboy = new Busboy({headers: req.headers});
        busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
          console.log('File [' + fieldname + ']: filename: ' + filename + ', encoding: ' + encoding + ', mimetype: ' + mimetype);
          file.on('data', function(data) {
            console.log('File [' + fieldname + '] got ' + data.length + ' bytes');
          });
          file.on('end', function() {
            console.log('File [' + fieldname + '] Finished');
          });
        });
        busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
          console.log('Field [' + fieldname + ']: value: ' + inspect(val));
        });
        busboy.on('finish', function() {
          console.log('Done parsing form!');
          //res.writeHead(303, { Connection: 'close', Location: '/' });
          res.end('success');
        });
        req.pipe(busboy);
      }
    }
  },
  {
  name: 'webpackDevMiddleware',
  filter: /\.(js|css)(\.map)?$/,
  handle: function(req, res, next) {
    handler = handler || webpackMiddleware(webpackCompiler, {
      publicPath: '/dist/',
      lazy: true,
      stats: {
        hash: false,
        cached: false,
        cachedAssets: false,
        colors: true
      }
    });
    try {
      return handler(req, res, next);
    } catch(e) {}
  }
}];
exports.writers = [
  'nico-jsx.PageWriter',
  'nico-jsx.StaticWriter',
  'nico-jsx.FileWriter'
];
// end settings }}

process.on('uncaughtException', function(err) {
  console.log(err);
});
<commit_msg>set webpack-dev-middleware lazy false because it is fast a lot<commit_after>var path = require('path');
var package = require('./package');
var webpack = require('webpack');
var inspect = require('util').inspect;
var Busboy = require('busboy');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackConfig = require('./webpack.config');
var webpackCompiler = webpack(webpackConfig);
var handler;

// {{ settings for nico
exports.site = {
  name: package.title,
  description: package.description,
  repo: package.repository.url,
  issues: package.bugs.url
};
exports.package = package;
exports.theme = 'site';
exports.source = process.cwd();
exports.output = path.join(process.cwd(), '_site');
exports.permalink = '{{directory}}/{{filename}}';
exports.ignorefilter = function(filepath, subdir) {
  var extname = path.extname(filepath);
  if (extname === '.tmp' || extname === '.bak') {
    return false;
  }
  if (/\.DS_Store/.test(filepath)) {
    return false;
  }
  if (/^(_site|_theme|node_modules|site|\.idea)/.test(subdir)) {
    return false;
  }
  return true;
};
exports.middlewares = [
  {
    name: 'upload',
    filter: /upload\.do?$/,
    handle: function(req, res, next) {
      if (req.method === 'POST') {
        var busboy = new Busboy({headers: req.headers});
        busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
          console.log('File [' + fieldname + ']: filename: ' + filename + ', encoding: ' + encoding + ', mimetype: ' + mimetype);
          file.on('data', function(data) {
            console.log('File [' + fieldname + '] got ' + data.length + ' bytes');
          });
          file.on('end', function() {
            console.log('File [' + fieldname + '] Finished');
          });
        });
        busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
          console.log('Field [' + fieldname + ']: value: ' + inspect(val));
        });
        busboy.on('finish', function() {
          console.log('Done parsing form!');
          //res.writeHead(303, { Connection: 'close', Location: '/' });
          res.end('success');
        });
        req.pipe(busboy);
      }
    }
  },
  {
  name: 'webpackDevMiddleware',
  filter: /\.(js|css)(\.map)?$/,
  handle: function(req, res, next) {
    handler = handler || webpackMiddleware(webpackCompiler, {
      publicPath: '/dist/',
      lazy: false,
      watchOptions: {
        aggregateTimeout: 300,
        poll: true
      },
      stats: {
        hash: false,
        cached: false,
        cachedAssets: false,
        colors: true
      }
    });
    try {
      return handler(req, res, next);
    } catch(e) {}
  }
}];
exports.writers = [
  'nico-jsx.PageWriter',
  'nico-jsx.StaticWriter',
  'nico-jsx.FileWriter'
];
// end settings }}

process.on('uncaughtException', function(err) {
  console.log(err);
});
<|endoftext|>


<commit_before>'use strict'

const types = {
  STATIC: 0,
  PARAM: 1,
  MATCH_ALL: 2,
  REGEX: 3,
  // It's used for a parameter, that is followed by another parameter in the same part
  MULTI_PARAM: 4
}

function Node (prefix, children, kind, handlers, regex) {
  this.prefix = prefix || '/'
  this.label = this.prefix[0]
  this.children = children || []
  this.numberOfChildren = this.children.length
  this.kind = kind || this.types.STATIC
  this.handlers = handlers || new Handlers()
  this.regex = regex || null
  this.wildcardChild = null
  this.parametricBrother = null
}

Object.defineProperty(Node.prototype, 'types', {
  value: types
})

Node.prototype.add = function (node) {
  if (node.kind === this.types.MATCH_ALL) {
    this.wildcardChild = node
  }

  this.children.push(node)
  this.children.sort((n1, n2) => n1.kind - n2.kind)
  this.numberOfChildren++

  // Search for a parametric brother and store it in a variable
  var parametricBrother = null
  for (var i = 0; i < this.numberOfChildren; i++) {
    const child = this.children[i]
    if ([this.types.PARAM, this.types.REGEX, this.types.MULTI_PARAM].indexOf(child.kind) > -1) {
      parametricBrother = child
      break
    }
  }

  // Save the parametric brother inside a static child
  for (i = 0; i < this.numberOfChildren; i++) {
    if (this.children[i].kind === this.types.STATIC && parametricBrother) {
      this.children[i].parametricBrother = parametricBrother
    }
  }
}

Node.prototype.findByLabel = function (label) {
  for (var i = 0; i < this.numberOfChildren; i++) {
    var child = this.children[i]
    if (child.label === label) {
      return child
    }
  }
  return null
}

Node.prototype.find = function (path, method) {
  for (var i = 0; i < this.numberOfChildren; i++) {
    var child = this.children[i]
    if (
      (child.numberOfChildren !== 0 || child.handlers[method] !== null) &&
      (child.kind !== 0 || path.startsWith(child.prefix))
    ) {
      return child
    }
  }
  return null
}

Node.prototype.setHandler = function (method, handler, params, store) {
  if (!handler) return

  this.handlers[method] = {
    handler: handler,
    params: params,
    store: store || null,
    paramsLength: params.length
  }
}

Node.prototype.getHandler = function (method) {
  return this.handlers[method]
}

Node.prototype.prettyPrint = function (prefix, tail) {
  var paramName = ''
  var handlers = this.handlers || {}
  var methods = Object.keys(handlers).filter(method => handlers[method] && handlers[method].handler)

  if (this.prefix === ':') {
    methods.forEach((method, index) => {
      var params = this.handlers[method].params
      var param = params[params.length - 1]
      if (methods.length > 1) {
        if (index === 0) {
          paramName += param + ` (${method})\n`
          return
        }
        paramName += '    ' + prefix + ':' + param + ` (${method})`
        paramName += (index === methods.length - 1 ? '' : '\n')
      } else {
        paramName = params[params.length - 1] + ` (${method})`
      }
    })
  } else if (methods.length) {
    paramName = ` (${methods.join('|')})`
  }

  var tree = `${prefix}${tail ? '└── ' : '├── '}${this.prefix}${paramName}\n`

  prefix = `${prefix}${tail ? '    ' : '│   '}`
  for (var i = 0; i < this.numberOfChildren - 1; i++) {
    tree += this.children[i].prettyPrint(prefix, false)
  }
  if (this.numberOfChildren > 0) {
    tree += this.children[this.numberOfChildren - 1].prettyPrint(prefix, true)
  }
  return tree
}

function Handlers (handlers) {
  handlers = handlers || {}
  this.DELETE = handlers.DELETE || null
  this.GET = handlers.GET || null
  this.HEAD = handlers.HEAD || null
  this.PATCH = handlers.PATCH || null
  this.POST = handlers.POST || null
  this.PUT = handlers.PUT || null
  this.OPTIONS = handlers.OPTIONS || null
  this.TRACE = handlers.TRACE || null
  this.CONNECT = handlers.CONNECT || null
}

module.exports = Node
module.exports.Handlers = Handlers
<commit_msg>Improve performance with path.slice<commit_after>'use strict'

const types = {
  STATIC: 0,
  PARAM: 1,
  MATCH_ALL: 2,
  REGEX: 3,
  // It's used for a parameter, that is followed by another parameter in the same part
  MULTI_PARAM: 4
}

function Node (prefix, children, kind, handlers, regex) {
  this.prefix = prefix || '/'
  this.label = this.prefix[0]
  this.children = children || []
  this.numberOfChildren = this.children.length
  this.kind = kind || this.types.STATIC
  this.handlers = handlers || new Handlers()
  this.regex = regex || null
  this.wildcardChild = null
  this.parametricBrother = null
}

Object.defineProperty(Node.prototype, 'types', {
  value: types
})

Node.prototype.add = function (node) {
  if (node.kind === this.types.MATCH_ALL) {
    this.wildcardChild = node
  }

  this.children.push(node)
  this.children.sort((n1, n2) => n1.kind - n2.kind)
  this.numberOfChildren++

  // Search for a parametric brother and store it in a variable
  var parametricBrother = null
  for (var i = 0; i < this.numberOfChildren; i++) {
    const child = this.children[i]
    if ([this.types.PARAM, this.types.REGEX, this.types.MULTI_PARAM].indexOf(child.kind) > -1) {
      parametricBrother = child
      break
    }
  }

  // Save the parametric brother inside a static child
  for (i = 0; i < this.numberOfChildren; i++) {
    if (this.children[i].kind === this.types.STATIC && parametricBrother) {
      this.children[i].parametricBrother = parametricBrother
    }
  }
}

Node.prototype.findByLabel = function (label) {
  for (var i = 0; i < this.numberOfChildren; i++) {
    var child = this.children[i]
    if (child.label === label) {
      return child
    }
  }
  return null
}

Node.prototype.find = function (path, method) {
  for (var i = 0; i < this.numberOfChildren; i++) {
    var child = this.children[i]
    if (
      (child.numberOfChildren !== 0 || child.handlers[method] !== null) &&
      (child.kind !== 0 || path.slice(0, child.prefix.length) === child.prefix)
    ) {
      return child
    }
  }
  return null
}

Node.prototype.setHandler = function (method, handler, params, store) {
  if (!handler) return

  this.handlers[method] = {
    handler: handler,
    params: params,
    store: store || null,
    paramsLength: params.length
  }
}

Node.prototype.getHandler = function (method) {
  return this.handlers[method]
}

Node.prototype.prettyPrint = function (prefix, tail) {
  var paramName = ''
  var handlers = this.handlers || {}
  var methods = Object.keys(handlers).filter(method => handlers[method] && handlers[method].handler)

  if (this.prefix === ':') {
    methods.forEach((method, index) => {
      var params = this.handlers[method].params
      var param = params[params.length - 1]
      if (methods.length > 1) {
        if (index === 0) {
          paramName += param + ` (${method})\n`
          return
        }
        paramName += '    ' + prefix + ':' + param + ` (${method})`
        paramName += (index === methods.length - 1 ? '' : '\n')
      } else {
        paramName = params[params.length - 1] + ` (${method})`
      }
    })
  } else if (methods.length) {
    paramName = ` (${methods.join('|')})`
  }

  var tree = `${prefix}${tail ? '└── ' : '├── '}${this.prefix}${paramName}\n`

  prefix = `${prefix}${tail ? '    ' : '│   '}`
  for (var i = 0; i < this.numberOfChildren - 1; i++) {
    tree += this.children[i].prettyPrint(prefix, false)
  }
  if (this.numberOfChildren > 0) {
    tree += this.children[this.numberOfChildren - 1].prettyPrint(prefix, true)
  }
  return tree
}

function Handlers (handlers) {
  handlers = handlers || {}
  this.DELETE = handlers.DELETE || null
  this.GET = handlers.GET || null
  this.HEAD = handlers.HEAD || null
  this.PATCH = handlers.PATCH || null
  this.POST = handlers.POST || null
  this.PUT = handlers.PUT || null
  this.OPTIONS = handlers.OPTIONS || null
  this.TRACE = handlers.TRACE || null
  this.CONNECT = handlers.CONNECT || null
}

module.exports = Node
module.exports.Handlers = Handlers
<|endoftext|>