<commit_before>// sia.js: a lightweight node wrapper for starting, and communicating with
// a Sia daemon (siad).
import BigNumber from 'bignumber.js'
import fs from 'fs'
import { spawn } from 'child_process'
import Path from 'path'
import rqueue from 'http-request-queue'

const concurrentRequestLimit = 100
const request = rqueue(concurrentRequestLimit).request

// sia.js error constants
export const errCouldNotConnect = new Error('could not connect to the Sia daemon')

// Siacoin -> hastings unit conversion functions
// These make conversion between units of Sia easy and consistent for developers.
// Never return exponentials from BigNumber.toString, since they confuse the API
BigNumber.config({ EXPONENTIAL_AT: 1e+9 })
BigNumber.config({ DECIMAL_PLACES: 30 })

const hastingsPerSiacoin = new BigNumber('10').toPower(24)
const siacoinsToHastings = (siacoins) => new BigNumber(siacoins).times(hastingsPerSiacoin)
const hastingsToSiacoins = (hastings) => new BigNumber(hastings).dividedBy(hastingsPerSiacoin)

// makeRequest takes an address and opts and returns a valid request.js request
// options object.
export const makeRequest = (address, opts) => {
	let callOptions = opts
	if (typeof opts === 'string') {
		callOptions = { url: opts }
	}
	callOptions.url = 'http://' + address + callOptions.url
	callOptions.json = true
	if (typeof callOptions.timeout === 'undefined') {
		callOptions.timeout = 10000
	}
	callOptions.headers = {
		'User-Agent': 'Sia-Agent',
	}

	return callOptions
}

// Call makes a call to the Sia API at `address`, with the request options defined by `opts`.
// returns a promise which resolves with the response if the request completes successfully
// and rejects with the error if the request fails.
const call = async (address, opts) => {
	const callOptions = makeRequest(address, opts)

	try {
		const res = await request(callOptions)
		if (res.response.statusCode < 200 || res.response.statusCode > 299) {
			throw res.body
		}
		return res.body
	} catch (e) {
		throw e
	}
}

// launch launches a new instance of siad using the flags defined by `settings`.
// this function can `throw`, callers should catch errors.
// callers should also handle the lifecycle of the spawned process.
const launch = (path, settings) => {
	const defaultSettings = {
		'api-addr': 'localhost:9980',
		'host-addr': ':9982',
		'rpc-addr': ':9981',
		'authenticate-api': false,
		'disable-api-security': false,
		'modules': 'cghmrtw',
	}
	const mergedSettings = Object.assign(defaultSettings, settings)
	const filterFlags = (key) => mergedSettings[key] !== false
	const mapFlags = (key) => '--' + key + '=' + mergedSettings[key]
	const flags = Object.keys(mergedSettings).filter(filterFlags).map(mapFlags)

	const siadOutput = (() => {
		if (typeof mergedSettings['sia-directory'] !== 'undefined') {
			return fs.createWriteStream(Path.join(mergedSettings['sia-directory'], 'siad-output.log'))
		}
		return fs.createWriteStream('siad-output.log')
	})()

	const opts = { }
	if (process.geteuid) {
		opts.uid = process.geteuid()
	}
	const siadProcess = spawn(path, flags, opts)
	siadProcess.stdout.pipe(siadOutput)
	siadProcess.stderr.pipe(siadOutput)
	return siadProcess
}

// isRunning returns true if a successful call can be to /gateway
// using the address provided in `address`.  Note that this call does not check
// whether the siad process is still running, it only checks if a Sia API is
// reachable.
async function isRunning(address) {
	try {
		await call(address, {
			url: '/gateway',
			timeout: 6e5, // 10 minutes
		})
		return true
	} catch (e) {
		return false
	}
}

// siadWrapper returns an instance of a Siad API configured with address.
const siadWrapper = (address) => {
	const siadAddress = address
	return {
		call: (options)  => call(siadAddress, options),
		isRunning: () => isRunning(siadAddress),
	}
}

// connect connects to a running Siad at `address` and returns a siadWrapper object.
async function connect(address) {
	const running = await isRunning(address)
	if (!running) {
		throw errCouldNotConnect
	}
	return siadWrapper(address)
}

export {
	connect,
	launch,
	isRunning,
	call,
	siacoinsToHastings,
	hastingsToSiacoins,
}
<commit_msg>use a configurable request queue size, decrease default<commit_after>// sia.js: a lightweight node wrapper for starting, and communicating with
// a Sia daemon (siad).
import BigNumber from 'bignumber.js'
import fs from 'fs'
import { spawn } from 'child_process'
import Path from 'path'
import rqueue from 'http-request-queue'

const defaultConcurrentRequestLimit = 40
let requestQueue = rqueue(defaultConcurrentRequestLimit)

// sia.js error constants
export const errCouldNotConnect = new Error('could not connect to the Sia daemon')

// Siacoin -> hastings unit conversion functions
// These make conversion between units of Sia easy and consistent for developers.
// Never return exponentials from BigNumber.toString, since they confuse the API
BigNumber.config({ EXPONENTIAL_AT: 1e+9 })
BigNumber.config({ DECIMAL_PLACES: 30 })

const hastingsPerSiacoin = new BigNumber('10').toPower(24)
const siacoinsToHastings = (siacoins) => new BigNumber(siacoins).times(hastingsPerSiacoin)
const hastingsToSiacoins = (hastings) => new BigNumber(hastings).dividedBy(hastingsPerSiacoin)

// makeRequest takes an address and opts and returns a valid request.js request
// options object.
export const makeRequest = (address, opts) => {
	let callOptions = opts
	if (typeof opts === 'string') {
		callOptions = { url: opts }
	}
	callOptions.url = 'http://' + address + callOptions.url
	callOptions.json = true
	if (typeof callOptions.timeout === 'undefined') {
		callOptions.timeout = 10000
	}
	callOptions.headers = {
		'User-Agent': 'Sia-Agent',
	}

	return callOptions
}

// Call makes a call to the Sia API at `address`, with the request options defined by `opts`.
// returns a promise which resolves with the response if the request completes successfully
// and rejects with the error if the request fails.
const call = async (address, opts) => {
	const callOptions = makeRequest(address, opts)

	try {
		const res = await requestQueue.request(callOptions)
		if (res.response.statusCode < 200 || res.response.statusCode > 299) {
			throw res.body
		}
		return res.body
	} catch (e) {
		throw e
	}
}

// launch launches a new instance of siad using the flags defined by `settings`.
// this function can `throw`, callers should catch errors.
// callers should also handle the lifecycle of the spawned process.
const launch = (path, settings) => {
	const defaultSettings = {
		'api-addr': 'localhost:9980',
		'host-addr': ':9982',
		'rpc-addr': ':9981',
		'authenticate-api': false,
		'disable-api-security': false,
		'modules': 'cghmrtw',
	}
	const mergedSettings = Object.assign(defaultSettings, settings)
	const filterFlags = (key) => mergedSettings[key] !== false
	const mapFlags = (key) => '--' + key + '=' + mergedSettings[key]
	const flags = Object.keys(mergedSettings).filter(filterFlags).map(mapFlags)

	const siadOutput = (() => {
		if (typeof mergedSettings['sia-directory'] !== 'undefined') {
			return fs.createWriteStream(Path.join(mergedSettings['sia-directory'], 'siad-output.log'))
		}
		return fs.createWriteStream('siad-output.log')
	})()

	const opts = { }
	if (process.geteuid) {
		opts.uid = process.geteuid()
	}
	const siadProcess = spawn(path, flags, opts)
	siadProcess.stdout.pipe(siadOutput)
	siadProcess.stderr.pipe(siadOutput)
	return siadProcess
}

// isRunning returns true if a successful call can be to /gateway
// using the address provided in `address`.  Note that this call does not check
// whether the siad process is still running, it only checks if a Sia API is
// reachable.
async function isRunning(address) {
	try {
		await call(address, {
			url: '/gateway',
			timeout: 6e5, // 10 minutes
		})
		return true
	} catch (e) {
		return false
	}
}

// siadWrapper returns an instance of a Siad API configured with address.
const siadWrapper = (address) => {
	const siadAddress = address
	return {
		call: (options)  => call(siadAddress, options),
		isRunning: () => isRunning(siadAddress),
	}
}

// connect connects to a running Siad at `address` and returns a siadWrapper object.
async function connect(address) {
	const running = await isRunning(address)
	if (!running) {
		throw errCouldNotConnect
	}
	return siadWrapper(address)
}

// setConcurrentRequestLimit limits the number of in-flight http requests,
// useful for applications that do lots of polling.
export const setConcurrentRequestLimit = (nrequests) => {
	requestQueue = rqueue(nrequests)
}

export {
	connect,
	launch,
	isRunning,
	call,
	siacoinsToHastings,
	hastingsToSiacoins,
}
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var pkg = require('../package.json');
var sweet = require("./sweet.js");
var syn = require("./syntax.js");

var argv = require("optimist")
    .usage("Usage: sjs [options] path/to/file.js")
    .alias('v', 'version')
    .describe('v', 'Output version info')
    .boolean('version')
    .alias('o', 'output')
    .describe('o', 'Output file path')
    .alias('m', 'module')
    .describe('m', 'use a module file for loading macro definitions. Use ./ or ../ for relative path otherwise looks up in installed npm packages')
    .alias('w', 'watch')
    .describe('w', 'watch a file')
    .boolean('watch')
    .alias('t', 'tokens')
    .describe('t', 'just emit the expanded tokens without parsing an AST')
    .alias('a', 'ast')
    .describe('a', 'just emit the expanded AST')
    .alias('p', 'no-parse')
    .describe('p', 'print out the expanded result but do not run through the parser (or apply hygienic renamings)')
    .boolean("no-parse")
    .alias('s', 'stdin')
    .describe('s', 'read from stdin')
    .boolean('stdin')
    .alias('c', 'sourcemap')
    .describe('c', 'generate a sourcemap')
    .boolean("sourcemap")
    .alias('n', 'num-expands')
    .describe('n', 'the maximum number of expands to perform')
    .alias('h', 'step-hygiene')
    .describe('h', 'display hygienic renames when stepping with "--num-expands"')
    .alias('r', 'readable-names')
    .describe('r', 'remove as many hygienic renames as possible (ES5 code only!)')
    .boolean('readable-names')
    .describe('format-indent', 'number of spaces for indentation')
    .alias('l', 'load-readtable')
    .describe('load-readtable', 'readtable module to install')
    .argv;

exports.run = function() {
    if (argv.version){
        return console.log("Sweet.js version: " + pkg.version);
    }
    var infile = argv._[0];
    var outfile = argv.output;
    var watch = argv.watch;
    var tokens = argv.tokens;
    var ast = argv.ast;
    var sourcemap = argv.sourcemap;
    var noparse = argv['no-parse'];
    var numexpands = argv['num-expands'];
    var displayHygiene = argv['step-hygiene'];
    var readableNames = argv['readable-names'];
    var formatIndent = parseInt(argv['format-indent'], 10);
    var readtableModules = argv['load-readtable'];
    if (formatIndent !== formatIndent) {
        formatIndent = 4;
    }

    var file;
    if (infile) {
        file = fs.readFileSync(infile, "utf8");
    } else if (argv.stdin) {
        file = fs.readFileSync("/dev/stdin", "utf8");
    } else if (argv._.length === 0) {
        console.log(require("optimist").help());
        return;
    }

    var cwd = process.cwd();
    var modules = typeof argv.module === 'string' ? [argv.module] : argv.module;

    modules = (modules || []).map(function(path) {
        return sweet.loadNodeModule(cwd, path);
    });

    if(readtableModules) {
        readtableModules = (Array.isArray(readtableModules) ?
                            readtableModules :
                            [readtableModules]);

        readtableModules.forEach(function(mod) {
            sweet.setReadtable(mod);
        });
    }

    var options = {
        filename: infile,
        modules: modules,
        ast: ast,
        readableNames: readableNames,
        escodegen: {
            format: {
                indent: {
                    style: Array(formatIndent + 1).join(' ')
                }
            }
        }
    };

    if (watch && outfile) {
        fs.watch(infile, function(){
            file = fs.readFileSync(infile, "utf8");
            try {
                fs.writeFileSync(outfile, sweet.compile(file, options).code, "utf8");
            } catch (e) {
                console.log(e);
            }
        });
    } else if (outfile) {
        if (sourcemap) {
            options.sourceMap = true;
            var result = sweet.compile(file, options);
            var mapfile = path.basename(outfile) + ".map";
            fs.writeFileSync(outfile, result.code + "\n//# sourceMappingURL=" + mapfile, "utf8");
            fs.writeFileSync(outfile + ".map", result.sourceMap, "utf8");
        } else {
            fs.writeFileSync(outfile, sweet.compile(file, options).code, "utf8");
        }
    } else if (tokens) {
        console.log(sweet.expand(file, modules, { maxExpands: numexpands }));
    } else if (ast) {
        console.log(JSON.stringify(sweet.compile(file, options), null, formatIndent));
    } else if (noparse) {
        var expanded = sweet.expand(file, modules, { maxExpands: numexpands });
        var unparsedString = syn.prettyPrint(expanded, displayHygiene);
        console.log(unparsedString);
    } else {
        options.maxExpands = numexpands;
        console.log(sweet.compile(file, options).code);
    }
};
<commit_msg>allow watch with sourcemap<commit_after>var fs = require("fs");
var path = require("path");
var pkg = require('../package.json');
var sweet = require("./sweet.js");
var syn = require("./syntax.js");

var argv = require("optimist")
    .usage("Usage: sjs [options] path/to/file.js")
    .alias('v', 'version')
    .describe('v', 'Output version info')
    .boolean('version')
    .alias('o', 'output')
    .describe('o', 'Output file path')
    .alias('m', 'module')
    .describe('m', 'use a module file for loading macro definitions. Use ./ or ../ for relative path otherwise looks up in installed npm packages')
    .alias('w', 'watch')
    .describe('w', 'watch a file')
    .boolean('watch')
    .alias('t', 'tokens')
    .describe('t', 'just emit the expanded tokens without parsing an AST')
    .alias('a', 'ast')
    .describe('a', 'just emit the expanded AST')
    .alias('p', 'no-parse')
    .describe('p', 'print out the expanded result but do not run through the parser (or apply hygienic renamings)')
    .boolean("no-parse")
    .alias('s', 'stdin')
    .describe('s', 'read from stdin')
    .boolean('stdin')
    .alias('c', 'sourcemap')
    .describe('c', 'generate a sourcemap')
    .boolean("sourcemap")
    .alias('n', 'num-expands')
    .describe('n', 'the maximum number of expands to perform')
    .alias('h', 'step-hygiene')
    .describe('h', 'display hygienic renames when stepping with "--num-expands"')
    .alias('r', 'readable-names')
    .describe('r', 'remove as many hygienic renames as possible (ES5 code only!)')
    .boolean('readable-names')
    .describe('format-indent', 'number of spaces for indentation')
    .alias('l', 'load-readtable')
    .describe('load-readtable', 'readtable module to install')
    .argv;

exports.run = function() {
    if (argv.version){
        return console.log("Sweet.js version: " + pkg.version);
    }
    var infile = argv._[0];
    var outfile = argv.output;
    var watch = argv.watch;
    var tokens = argv.tokens;
    var ast = argv.ast;
    var sourcemap = argv.sourcemap;
    var noparse = argv['no-parse'];
    var numexpands = argv['num-expands'];
    var displayHygiene = argv['step-hygiene'];
    var readableNames = argv['readable-names'];
    var formatIndent = parseInt(argv['format-indent'], 10);
    var readtableModules = argv['load-readtable'];
    if (formatIndent !== formatIndent) {
        formatIndent = 4;
    }

    var file;
    if (infile) {
        file = fs.readFileSync(infile, "utf8");
    } else if (argv.stdin) {
        file = fs.readFileSync("/dev/stdin", "utf8");
    } else if (argv._.length === 0) {
        console.log(require("optimist").help());
        return;
    }

    var cwd = process.cwd();
    var modules = typeof argv.module === 'string' ? [argv.module] : argv.module;

    modules = (modules || []).map(function(path) {
        return sweet.loadNodeModule(cwd, path);
    });

    if(readtableModules) {
        readtableModules = (Array.isArray(readtableModules) ?
                            readtableModules :
                            [readtableModules]);

        readtableModules.forEach(function(mod) {
            sweet.setReadtable(mod);
        });
    }

    var options = {
        filename: infile,
        modules: modules,
        ast: ast,
        readableNames: readableNames,
        escodegen: {
            format: {
                indent: {
                    style: Array(formatIndent + 1).join(' ')
                }
            }
        }
    };

    var doCompile
    if (sourcemap) {
        doCompile = function () {
            options.sourceMap = true;
            var result = sweet.compile(file, options);
            var mapfile = path.basename(outfile) + ".map";
            fs.writeFileSync(outfile, result.code + "\n//# sourceMappingURL=" + mapfile, "utf8");
            fs.writeFileSync(outfile + ".map", result.sourceMap, "utf8");
        }
    } else {
        doCompile = function () {
            fs.writeFileSync(outfile, sweet.compile(file, options).code, "utf8");
        }
    }
    if (watch && outfile) {
        fs.watch(infile, function(){
            file = fs.readFileSync(infile, "utf8");
            try {
                doCompile();
            } catch (e) {
                console.log(e);
            }
        });
    } else if (outfile) {
        doCompile();
    } else if (tokens) {
        console.log(sweet.expand(file, modules, { maxExpands: numexpands }));
    } else if (ast) {
        console.log(JSON.stringify(sweet.compile(file, options), null, formatIndent));
    } else if (noparse) {
        var expanded = sweet.expand(file, modules, { maxExpands: numexpands });
        var unparsedString = syn.prettyPrint(expanded, displayHygiene);
        console.log(unparsedString);
    } else {
        options.maxExpands = numexpands;
        console.log(sweet.compile(file, options).code);
    }
};
<|endoftext|>


<commit_before>import _ from 'underscore';
import util from 'util';
import path from 'path';
import assert from 'assert';
import chalk from 'chalk';

import isopackets from '../tool-env/isopackets.js'
import files from '../fs/files.js';
import utils from '../utils/utils.js';
import { Console } from '../console/console.js';
import buildmessage from '../utils/buildmessage.js';
import main from '../cli/main.js';
import httpHelpers from '../utils/http-helpers.js';

import { AVAILABLE_PLATFORMS, displayNameForPlatform } from './index.js';
import { CordovaBuilder } from './builder.js';

function loadDependenciesFromCordovaPackageIfNeeded() {
  if (typeof Cordova !== 'undefined') return;

  ({ Cordova } = isopackets.load('cordova-support').cordova);
  ({ cordova, events, CordovaError, superspawn, cordova_util, PluginInfoProvider } = Cordova);

  events.on('results', logIfVerbose);
  events.on('log', logIfVerbose);
  events.on('warn', log);
  // events.on('verbose', logIfVerbose);
}

function logIfVerbose(...args) {
  if (Console.verbose) {
    log(...args);
  }
};

function log(...args) {
  Console.rawInfo(`%% ${util.format.apply(null, args)}\n`);
}

export class CordovaProject {
  constructor(projectContext, appName = files.pathBasename(projectContext.projectDir)) {
    loadDependenciesFromCordovaPackageIfNeeded();

    this.projectContext = projectContext;

    this.projectRoot = projectContext.getProjectLocalDirectory('cordova-build');
    this.appName = appName;

    this.pluginsDir = files.pathJoin(this.projectRoot, 'plugins');

    this.createIfNeeded();
  }

  createIfNeeded() {
    if (!files.exists(this.projectRoot)) {
      files.mkdir_p(files.pathDirname(this.projectRoot));
      // Cordova app identifiers have to look like Java namespaces.
      // Change weird characters (especially hyphens) into underscores.
      const appId = 'com.meteor.userapps.' + this.appName.replace(/[^a-zA-Z\d_$.]/g, '_');

      // Don't set cwd to project root in runCommands because it doesn't exist yet
      this.runCommands('creating Cordova project', async () => {
        await cordova.raw.create(files.convertToOSPath(this.projectRoot), appId, this.appName);
      }, undefined, null);
    }
  }

  // Preparing

  prepareFromAppBundle(bundlePath, plugins, options = {}) {
    assert(bundlePath);
    assert(plugins);

    Console.debug('Preparing Cordova project from app bundle');

    const builder = new CordovaBuilder(this, bundlePath, plugins, options);
    builder.start();
  }

  prepareForPlatform(platform) {
    assert(platform);

    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform] });

    this.runCommands(`preparing Cordova project for platform \
${displayNameForPlatform(platform)}`, async () => {
      await cordova.raw.prepare(commandOptions);
    });
  }

  // Building (includes prepare)

  buildForPlatform(platform, options = [], extraPaths) {
    assert(platform);

    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform], options: options });

    this.runCommands(`building Cordova project for platform \
${displayNameForPlatform(platform)}`, async () => {
      await cordova.raw.build(commandOptions);
    });
  }

  // Running (incudes build)

  async run(platform, isDevice, options = [], extraPaths) {
    options.push(isDevice ? '--device' : '--emulator');

    const env = this.defaultEnvWithPathsAdded(...extraPaths);
    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform], options: options });

    this.runCommands(`running Cordova project for platform \
${displayNameForPlatform(platform)} with options ${options}`, async () => {
      await cordova.raw.run(commandOptions);
    }, env);
  }

  // Platforms

  checkPlatformRequirements(platform) {
    if (platform === 'ios' && process.platform !== 'darwin') {
      Console.warn("Currently, it is only possible to build iOS apps on an OS X system.");
      return false;
    }

    const installedPlatforms = this.listInstalledPlatforms();
    const inProject = _.contains(installedPlatforms, platform);
    if (!inProject) {
      Console.warn(`Please add the ${displayNameForPlatform(platform)} \
platform to your project first.`);
      Console.info(`Run: ${Console.command(`meteor add-platform ${platform}`)}`);
      return false;
    }

    const allRequirements = this.runCommands(`checking Cordova \
requirements for platform ${displayNameForPlatform(platform)}`,
      async () => {
        return await cordova.raw.requirements([platform], this.defaultOptions);
      });
    let requirements = allRequirements && allRequirements[platform];
    if (!requirements) {
      Console.error(`Failed to check requirements for platform \
${displayNameForPlatform(platform)}`);
      return false;
    } else if (requirements instanceof CordovaError) {
      Console.error(`cordova: ${requirements.message}`);
      return false;
    }

    // We don't use ios-deploy, but open Xcode to run on a device instead
    requirements = _.reject(requirements, requirement => requirement.id === 'ios-deploy');

    const satisfied = _.every(requirements, requirement => requirement.installed);
    if (!satisfied) {
      Console.info();
      Console.info(`Make sure all installation requirements are satisfied \
before running or building for ${displayNameForPlatform(platform)}:`);
      for (requirement of requirements) {
        const name = requirement.name;
        if (requirement.installed) {
          Console.success(name);
        } else {
          const reason = requirement.metadata && requirement.metadata.reason;
          if (reason) {
            Console.failInfo(`${name}: ${reason}`);
          } else {
            Console.failInfo(name);
          }
        }
      }
    }
    return satisfied;
  }

  listInstalledPlatforms() {
    return cordova_util.listPlatforms(files.convertToOSPath(this.projectRoot));
  }

  updatePlatforms(platforms = this.listInstalledPlatforms()) {
    this.runCommands(`updating Cordova project for platforms \
${displayNamesForPlatforms(platform)}`, async () => {
      await cordova.raw.platform('update', platforms, this.defaultOptions);
    });
  }

  addPlatform(platform) {
    this.runCommands(`adding platform ${displayNameForPlatform(platform)} \
to Cordova project`, async () => {
      await cordova.raw.platform('add', platform, this.defaultOptions);
    });
  }

  removePlatform(platform) {
    this.runCommands(`removing platform ${displayNameForPlatform(platform)} \
from Cordova project`, async () => {
      await cordova.raw.platform('rm', platform, this.defaultOptions);
    });
  }

  get cordovaPlatformsInApp() {
    return this.projectContext.platformList.getCordovaPlatforms();
  }

  // Ensures that the Cordova platforms are synchronized with the app-level
  // platforms.
  ensurePlatformsAreSynchronized(platforms = this.cordovaPlatformsInApp) {
    buildmessage.assertInCapture();

    const installedPlatforms = this.listInstalledPlatforms();

    for (platform of platforms) {
      if (_.contains(installedPlatforms, platform)) continue;

      this.addPlatform(platform);
    }

    for (platform of installedPlatforms) {
      if (!_.contains(platforms, platform) &&
        _.contains(AVAILABLE_PLATFORMS, platform)) {
        this.removePlatform(platform);
      }
    }
  }

  // Plugins

  listInstalledPlugins() {
    const pluginInfoProvider = new PluginInfoProvider();
    const installedPlugins = pluginInfoProvider.getAllWithinSearchPath(
      files.convertToOSPath(this.pluginsDir));
    const fetchedPlugins = this.listFetchedPlugins();
    return _.object(installedPlugins.map(plugin => {
      const id = plugin.id;
      const version = fetchedPlugins[id] || plugin.version;
      return [id, version];
    }));
  }

  listFetchedPlugins() {
    const fetchedPluginsMetadata = JSON.parse(files.readFile(
      files.pathJoin(this.pluginsDir, 'fetch.json'), 'utf8'));
    return _.object(_.map(fetchedPluginsMetadata, (metadata, name) => {
      const source = metadata.source;
      let version;
      if (source.type === 'registry') {
        version = source.id.split('@')[1];
      } else if (source.type === 'git') {
        version = `${source.url}#${source.ref}`;
      } else if (source.type === 'local') {
        version = source.path;
      }
      return [name, version];
    }));
  }

  convertToGitUrl(url) {
    // Matches GitHub tarball URLs, like:
    // https://github.com/meteor/com.meteor.cordova-update/tarball/92fe99b7248075318f6446b288995d4381d24cd2
    const match =
      url.match(/^https?:\/\/github.com\/(.+?)\/(.+?)\/tarball\/([0-9a-f]{40})/);
    if (match) {
        const [,organization,repository,sha] = match;
      // Convert them to a Git URL
      return `https://github.com/${organization}/${repository}.git#${sha}`;
    } else if (/\.git/.test(url)) {
      return url;
    } else {
      buildmessage.error(`Meteor no longer supports installing Cordova plugins \
from arbitrary tarball URLs. You can either add a plugin from a Git URL with \
a SHA reference, or from a local path. (Attempting to install from ${url}.)`);
      return null;
    }
  }

  targetForPlugin(name, version) {
    if (!version) {
      return name;
    }

    if (utils.isUrlWithSha(version)) {
      return this.convertToGitUrl(version);
    } else if (utils.isUrlWithFileScheme(version)) {
      // Strip file:// and resolve the path relative to the cordova-build
      // directory
      const pluginPath = this.resolveLocalPluginPath(version);
      // We need to check if the directory exists ourselves because Cordova
      // will try to install from npm (and fail with an unhelpful error message)
      // if the directory is not found
      if (!cordova_util.isDirectory(pluginPath)) {
        buildmessage.error(`Couldn't find local directory \
'${pluginPath}'. (Attempting to install plugin ${name}).`);
        return null;
      }
      return files.convertToOSPath(pluginPath);
    } else {
      return `${name}@${version}`;
    }
  }

  addPlugin(name, version, config) {
    const target = this.targetForPlugin(name, version);
    if (target) {
      // TODO Pass config attributes to Cordova command

      this.runCommands(`adding plugin ${target} \
to Cordova project`, async () => {
        await cordova.raw.plugin('add', target, this.defaultOptions);
      });
    }
  }

  removePlugins(plugins) {
    if (_.isEmpty(plugins)) return;

    this.runCommands(`removing plugins ${plugins} \
from Cordova project`, async () => {
      await cordova.raw.plugin('rm', plugins, this.defaultOptions);
    });
  }

  // Strips the file:// from the path and if a relative path was used, it translates it to a relative path to the
  // cordova-build directory instead of meteor project directory.
  resolveLocalPluginPath(pluginPath) {
    pluginPath = pluginPath.substr("file://".length);
    if (utils.isPathRelative(pluginPath)) {
      return path.resolve(this.projectContext.projectDir, pluginPath);
    } else {
      return pluginPath;
    }
  }

  // Ensures that the Cordova plugins are synchronized with the app-level
  // plugins.
  ensurePluginsAreSynchronized(plugins, pluginsConfiguration = {}) {
    assert(plugins);

    buildmessage.assertInCapture();

    buildmessage.enterJob({ title: "installing Cordova plugins"}, () => {
      const installedPlugins = this.listInstalledPlugins();

      // Due to the dependency structure of Cordova plugins, it is impossible to
      // upgrade the version on an individual Cordova plugin. Instead, whenever a
      // new Cordova plugin is added or removed, or its version is changed,
      // we just reinstall all of the plugins.
      let shouldReinstallAllPlugins = false;

      // Iterate through all of the plugins and find if any of them have a new
      // version. Additionally check if we have plugins installed from local path.
      const pluginsFromLocalPath = {};
      _.each(plugins, (version, name) => {
        // Check if plugin is installed from local path
        const isPluginFromLocalPath = utils.isUrlWithFileScheme(version);

        if (isPluginFromLocalPath) {
          pluginsFromLocalPath[name] = version;
        } else {
          if (utils.isUrlWithSha(version)) {
            version = this.convertToGitUrl(version);
          }

          if (!_.has(installedPlugins, name) ||
            installedPlugins[name] !== version) {
            // We do not have the plugin installed or the version has changed.
            shouldReinstallAllPlugins = true;
          }
        }
      });

      if (!_.isEmpty(pluginsFromLocalPath)) {
        Console.debug('Reinstalling Cordova plugins added from the local path');
      }

      // Check to see if we have any installed plugins that are not in the current
      // set of plugins.
      _.each(installedPlugins, (version, name) => {
        if (!_.has(plugins, name)) {
          shouldReinstallAllPlugins = true;
        }
      });

      if (shouldReinstallAllPlugins || !_.isEmpty(pluginsFromLocalPath)) {
        let pluginsToRemove;
        if (shouldReinstallAllPlugins) {
          pluginsToRemove = Object.keys(installedPlugins);
        } else {
          // Only try to remove plugins that are currently installed
          pluginsToRemove = _.intersection(
            Object.keys(pluginsFromLocalPath),
            Object.keys(installedPlugins));
        }

        this.removePlugins(pluginsToRemove);

        // Now install necessary plugins.

        if (shouldReinstallAllPlugins) {
          pluginsToInstall = plugins;
        } else {
          pluginsToInstall = pluginsFromLocalPath;
        }

        const pluginsCount = _.size(pluginsToInstall);
        let pluginsInstalled = 0;

        buildmessage.reportProgress({ current: 0, end: pluginsCount });
        _.each(pluginsToInstall, (version, name) => {
          this.addPlugin(name, version, pluginsConfiguration[name]);

          if (buildmessage.jobHasMessages()) return;

          buildmessage.reportProgress({
            current: ++pluginsInstalled,
            end: pluginsCount
          });
        });
      }
    });
  }

  // Cordova commands support

  get defaultOptions() {
    return { silent: !Console.verbose, verbose: Console.verbose };
  }

  defaultEnvWithPathsAdded(...extraPaths) {
    let paths = (this.defaultPaths || []);
    paths.unshift(...extraPaths);
    const env = files.currentEnvWithPathsAdded(...paths);
    return env;
  }

  get defaultPaths() {
    const nodeBinDir = files.getCurrentNodeBinDir();
    return [nodeBinDir];
  }

  runCommands(title, asyncFunc, env = this.defaultEnvWithPathsAdded(),
    cwd = this.projectRoot) {
    // Capitalize title for debug output
    Console.debug(title[0].toUpperCase() + title.slice(1));

    const oldCwd = process.cwd();
    if (cwd) {
      process.chdir(files.convertToOSPath(cwd));
    }

    superspawn.setEnv(env);

    try {
      return Promise.await(asyncFunc());
    } catch (error) {
      if (error instanceof CordovaError) {
        Console.arrowError('Errors executing Cordova commands:');
        Console.error();
        const consoleOptions = Console.options({ indent: 3 });
        Console.error(`While ${title}:`, consoleOptions);
        Console.error();
        const errorMessage = Console.verbose ? (error.stack || error.message) :
          error.message;
        Console.error(errorMessage, consoleOptions);
        Console.error(chalk.green("(If the error message contains suggestions \
for a fix, note that this may not apply to the Meteor integration. You can try \
running again with the --verbose option to help diagnose the issue.)"),
          consoleOptions);
        throw new main.ExitWithCode(1);
      } else {
        throw error;
      }
    } finally {
      if (oldCwd) {
        process.chdir(oldCwd);
      }
    }
  }
}
<commit_msg>Pass config attributes to plugin add<commit_after>import _ from 'underscore';
import util from 'util';
import path from 'path';
import assert from 'assert';
import chalk from 'chalk';

import isopackets from '../tool-env/isopackets.js'
import files from '../fs/files.js';
import utils from '../utils/utils.js';
import { Console } from '../console/console.js';
import buildmessage from '../utils/buildmessage.js';
import main from '../cli/main.js';
import httpHelpers from '../utils/http-helpers.js';

import { AVAILABLE_PLATFORMS, displayNameForPlatform } from './index.js';
import { CordovaBuilder } from './builder.js';

function loadDependenciesFromCordovaPackageIfNeeded() {
  if (typeof Cordova !== 'undefined') return;

  ({ Cordova } = isopackets.load('cordova-support').cordova);
  ({ cordova, events, CordovaError, superspawn, cordova_util, PluginInfoProvider } = Cordova);

  events.on('results', logIfVerbose);
  events.on('log', logIfVerbose);
  events.on('warn', log);
  // events.on('verbose', logIfVerbose);
}

function logIfVerbose(...args) {
  if (Console.verbose) {
    log(...args);
  }
};

function log(...args) {
  Console.rawInfo(`%% ${util.format.apply(null, args)}\n`);
}

export class CordovaProject {
  constructor(projectContext, appName = files.pathBasename(projectContext.projectDir)) {
    loadDependenciesFromCordovaPackageIfNeeded();

    this.projectContext = projectContext;

    this.projectRoot = projectContext.getProjectLocalDirectory('cordova-build');
    this.appName = appName;

    this.pluginsDir = files.pathJoin(this.projectRoot, 'plugins');

    this.createIfNeeded();
  }

  createIfNeeded() {
    if (!files.exists(this.projectRoot)) {
      files.mkdir_p(files.pathDirname(this.projectRoot));
      // Cordova app identifiers have to look like Java namespaces.
      // Change weird characters (especially hyphens) into underscores.
      const appId = 'com.meteor.userapps.' + this.appName.replace(/[^a-zA-Z\d_$.]/g, '_');

      // Don't set cwd to project root in runCommands because it doesn't exist yet
      this.runCommands('creating Cordova project', async () => {
        await cordova.raw.create(files.convertToOSPath(this.projectRoot), appId, this.appName);
      }, undefined, null);
    }
  }

  // Preparing

  prepareFromAppBundle(bundlePath, plugins, options = {}) {
    assert(bundlePath);
    assert(plugins);

    Console.debug('Preparing Cordova project from app bundle');

    const builder = new CordovaBuilder(this, bundlePath, plugins, options);
    builder.start();
  }

  prepareForPlatform(platform) {
    assert(platform);

    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform] });

    this.runCommands(`preparing Cordova project for platform \
${displayNameForPlatform(platform)}`, async () => {
      await cordova.raw.prepare(commandOptions);
    });
  }

  // Building (includes prepare)

  buildForPlatform(platform, options = [], extraPaths) {
    assert(platform);

    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform], options: options });

    this.runCommands(`building Cordova project for platform \
${displayNameForPlatform(platform)}`, async () => {
      await cordova.raw.build(commandOptions);
    });
  }

  // Running (incudes build)

  async run(platform, isDevice, options = [], extraPaths) {
    options.push(isDevice ? '--device' : '--emulator');

    const env = this.defaultEnvWithPathsAdded(...extraPaths);
    const commandOptions = _.extend(this.defaultOptions,
      { platforms: [platform], options: options });

    this.runCommands(`running Cordova project for platform \
${displayNameForPlatform(platform)} with options ${options}`, async () => {
      await cordova.raw.run(commandOptions);
    }, env);
  }

  // Platforms

  checkPlatformRequirements(platform) {
    if (platform === 'ios' && process.platform !== 'darwin') {
      Console.warn("Currently, it is only possible to build iOS apps on an OS X system.");
      return false;
    }

    const installedPlatforms = this.listInstalledPlatforms();
    const inProject = _.contains(installedPlatforms, platform);
    if (!inProject) {
      Console.warn(`Please add the ${displayNameForPlatform(platform)} \
platform to your project first.`);
      Console.info(`Run: ${Console.command(`meteor add-platform ${platform}`)}`);
      return false;
    }

    const allRequirements = this.runCommands(`checking Cordova \
requirements for platform ${displayNameForPlatform(platform)}`,
      async () => {
        return await cordova.raw.requirements([platform], this.defaultOptions);
      });
    let requirements = allRequirements && allRequirements[platform];
    if (!requirements) {
      Console.error(`Failed to check requirements for platform \
${displayNameForPlatform(platform)}`);
      return false;
    } else if (requirements instanceof CordovaError) {
      Console.error(`cordova: ${requirements.message}`);
      return false;
    }

    // We don't use ios-deploy, but open Xcode to run on a device instead
    requirements = _.reject(requirements, requirement => requirement.id === 'ios-deploy');

    const satisfied = _.every(requirements, requirement => requirement.installed);
    if (!satisfied) {
      Console.info();
      Console.info(`Make sure all installation requirements are satisfied \
before running or building for ${displayNameForPlatform(platform)}:`);
      for (requirement of requirements) {
        const name = requirement.name;
        if (requirement.installed) {
          Console.success(name);
        } else {
          const reason = requirement.metadata && requirement.metadata.reason;
          if (reason) {
            Console.failInfo(`${name}: ${reason}`);
          } else {
            Console.failInfo(name);
          }
        }
      }
    }
    return satisfied;
  }

  listInstalledPlatforms() {
    return cordova_util.listPlatforms(files.convertToOSPath(this.projectRoot));
  }

  updatePlatforms(platforms = this.listInstalledPlatforms()) {
    this.runCommands(`updating Cordova project for platforms \
${displayNamesForPlatforms(platform)}`, async () => {
      await cordova.raw.platform('update', platforms, this.defaultOptions);
    });
  }

  addPlatform(platform) {
    this.runCommands(`adding platform ${displayNameForPlatform(platform)} \
to Cordova project`, async () => {
      await cordova.raw.platform('add', platform, this.defaultOptions);
    });
  }

  removePlatform(platform) {
    this.runCommands(`removing platform ${displayNameForPlatform(platform)} \
from Cordova project`, async () => {
      await cordova.raw.platform('rm', platform, this.defaultOptions);
    });
  }

  get cordovaPlatformsInApp() {
    return this.projectContext.platformList.getCordovaPlatforms();
  }

  // Ensures that the Cordova platforms are synchronized with the app-level
  // platforms.
  ensurePlatformsAreSynchronized(platforms = this.cordovaPlatformsInApp) {
    buildmessage.assertInCapture();

    const installedPlatforms = this.listInstalledPlatforms();

    for (platform of platforms) {
      if (_.contains(installedPlatforms, platform)) continue;

      this.addPlatform(platform);
    }

    for (platform of installedPlatforms) {
      if (!_.contains(platforms, platform) &&
        _.contains(AVAILABLE_PLATFORMS, platform)) {
        this.removePlatform(platform);
      }
    }
  }

  // Plugins

  listInstalledPlugins() {
    const pluginInfoProvider = new PluginInfoProvider();
    const installedPlugins = pluginInfoProvider.getAllWithinSearchPath(
      files.convertToOSPath(this.pluginsDir));
    const fetchedPlugins = this.listFetchedPlugins();
    return _.object(installedPlugins.map(plugin => {
      const id = plugin.id;
      const version = fetchedPlugins[id] || plugin.version;
      return [id, version];
    }));
  }

  listFetchedPlugins() {
    const fetchedPluginsMetadata = JSON.parse(files.readFile(
      files.pathJoin(this.pluginsDir, 'fetch.json'), 'utf8'));
    return _.object(_.map(fetchedPluginsMetadata, (metadata, name) => {
      const source = metadata.source;
      let version;
      if (source.type === 'registry') {
        version = source.id.split('@')[1];
      } else if (source.type === 'git') {
        version = `${source.url}#${source.ref}`;
      } else if (source.type === 'local') {
        version = source.path;
      }
      return [name, version];
    }));
  }

  convertToGitUrl(url) {
    // Matches GitHub tarball URLs, like:
    // https://github.com/meteor/com.meteor.cordova-update/tarball/92fe99b7248075318f6446b288995d4381d24cd2
    const match =
      url.match(/^https?:\/\/github.com\/(.+?)\/(.+?)\/tarball\/([0-9a-f]{40})/);
    if (match) {
        const [,organization,repository,sha] = match;
      // Convert them to a Git URL
      return `https://github.com/${organization}/${repository}.git#${sha}`;
    } else if (/\.git/.test(url)) {
      return url;
    } else {
      buildmessage.error(`Meteor no longer supports installing Cordova plugins \
from arbitrary tarball URLs. You can either add a plugin from a Git URL with \
a SHA reference, or from a local path. (Attempting to install from ${url}.)`);
      return null;
    }
  }

  targetForPlugin(name, version) {
    if (!version) {
      return name;
    }

    if (utils.isUrlWithSha(version)) {
      return this.convertToGitUrl(version);
    } else if (utils.isUrlWithFileScheme(version)) {
      // Strip file:// and resolve the path relative to the cordova-build
      // directory
      const pluginPath = this.resolveLocalPluginPath(version);
      // We need to check if the directory exists ourselves because Cordova
      // will try to install from npm (and fail with an unhelpful error message)
      // if the directory is not found
      if (!cordova_util.isDirectory(pluginPath)) {
        buildmessage.error(`Couldn't find local directory \
'${pluginPath}'. (Attempting to install plugin ${name}).`);
        return null;
      }
      return files.convertToOSPath(pluginPath);
    } else {
      return `${name}@${version}`;
    }
  }

  addPlugin(name, version, config = {}) {
    const target = this.targetForPlugin(name, version);
    if (target) {
      const commandOptions = _.extend(this.defaultOptions,
        { cli_variables: config });

      this.runCommands(`adding plugin ${target} \
to Cordova project`, async () => {
        await cordova.raw.plugin('add', [target], commandOptions);
      });
    }
  }

  removePlugins(plugins) {
    if (_.isEmpty(plugins)) return;

    this.runCommands(`removing plugins ${plugins} \
from Cordova project`, async () => {
      await cordova.raw.plugin('rm', plugins, this.defaultOptions);
    });
  }

  // Strips the file:// from the path and if a relative path was used, it translates it to a relative path to the
  // cordova-build directory instead of meteor project directory.
  resolveLocalPluginPath(pluginPath) {
    pluginPath = pluginPath.substr("file://".length);
    if (utils.isPathRelative(pluginPath)) {
      return path.resolve(this.projectContext.projectDir, pluginPath);
    } else {
      return pluginPath;
    }
  }

  // Ensures that the Cordova plugins are synchronized with the app-level
  // plugins.
  ensurePluginsAreSynchronized(plugins, pluginsConfiguration = {}) {
    assert(plugins);

    buildmessage.assertInCapture();

    buildmessage.enterJob({ title: "installing Cordova plugins"}, () => {
      const installedPlugins = this.listInstalledPlugins();

      // Due to the dependency structure of Cordova plugins, it is impossible to
      // upgrade the version on an individual Cordova plugin. Instead, whenever a
      // new Cordova plugin is added or removed, or its version is changed,
      // we just reinstall all of the plugins.
      let shouldReinstallAllPlugins = false;

      // Iterate through all of the plugins and find if any of them have a new
      // version. Additionally check if we have plugins installed from local path.
      const pluginsFromLocalPath = {};
      _.each(plugins, (version, name) => {
        // Check if plugin is installed from local path
        const isPluginFromLocalPath = utils.isUrlWithFileScheme(version);

        if (isPluginFromLocalPath) {
          pluginsFromLocalPath[name] = version;
        } else {
          if (utils.isUrlWithSha(version)) {
            version = this.convertToGitUrl(version);
          }

          if (!_.has(installedPlugins, name) ||
            installedPlugins[name] !== version) {
            // We do not have the plugin installed or the version has changed.
            shouldReinstallAllPlugins = true;
          }
        }
      });

      if (!_.isEmpty(pluginsFromLocalPath)) {
        Console.debug('Reinstalling Cordova plugins added from the local path');
      }

      // Check to see if we have any installed plugins that are not in the current
      // set of plugins.
      _.each(installedPlugins, (version, name) => {
        if (!_.has(plugins, name)) {
          shouldReinstallAllPlugins = true;
        }
      });

      if (shouldReinstallAllPlugins || !_.isEmpty(pluginsFromLocalPath)) {
        let pluginsToRemove;
        if (shouldReinstallAllPlugins) {
          pluginsToRemove = Object.keys(installedPlugins);
        } else {
          // Only try to remove plugins that are currently installed
          pluginsToRemove = _.intersection(
            Object.keys(pluginsFromLocalPath),
            Object.keys(installedPlugins));
        }

        this.removePlugins(pluginsToRemove);

        // Now install necessary plugins.

        if (shouldReinstallAllPlugins) {
          pluginsToInstall = plugins;
        } else {
          pluginsToInstall = pluginsFromLocalPath;
        }

        const pluginsCount = _.size(pluginsToInstall);
        let pluginsInstalled = 0;

        buildmessage.reportProgress({ current: 0, end: pluginsCount });
        _.each(pluginsToInstall, (version, name) => {
          this.addPlugin(name, version, pluginsConfiguration[name]);

          if (buildmessage.jobHasMessages()) return;

          buildmessage.reportProgress({
            current: ++pluginsInstalled,
            end: pluginsCount
          });
        });
      }
    });
  }

  // Cordova commands support

  get defaultOptions() {
    return { silent: !Console.verbose, verbose: Console.verbose };
  }

  defaultEnvWithPathsAdded(...extraPaths) {
    let paths = (this.defaultPaths || []);
    paths.unshift(...extraPaths);
    const env = files.currentEnvWithPathsAdded(...paths);
    return env;
  }

  get defaultPaths() {
    const nodeBinDir = files.getCurrentNodeBinDir();
    return [nodeBinDir];
  }

  runCommands(title, asyncFunc, env = this.defaultEnvWithPathsAdded(),
    cwd = this.projectRoot) {
    // Capitalize title for debug output
    Console.debug(title[0].toUpperCase() + title.slice(1));

    const oldCwd = process.cwd();
    if (cwd) {
      process.chdir(files.convertToOSPath(cwd));
    }

    superspawn.setEnv(env);

    try {
      return Promise.await(asyncFunc());
    } catch (error) {
      if (error instanceof CordovaError) {
        Console.arrowError('Errors executing Cordova commands:');
        Console.error();
        const consoleOptions = Console.options({ indent: 3 });
        Console.error(`While ${title}:`, consoleOptions);
        Console.error();
        const errorMessage = Console.verbose ? (error.stack || error.message) :
          error.message;
        Console.error(errorMessage, consoleOptions);
        Console.error(chalk.green("(If the error message contains suggestions \
for a fix, note that this may not apply to the Meteor integration. You can try \
running again with the --verbose option to help diagnose the issue.)"),
          consoleOptions);
        throw new main.ExitWithCode(1);
      } else {
        throw error;
      }
    } finally {
      if (oldCwd) {
        process.chdir(oldCwd);
      }
    }
  }
}
<|endoftext|>


<commit_before>'use strict'

const util = require('util')
const path = require('path')
const fs = require('fs')
const njk = require('nunjucks')

/**
 * Custom tag for Nunjucks. Renders a file with custom data.
 * @param {String} src - Path base for further injects with the inject tag.
 */
const InjectTag = function(src) {

	this.tags = [ 'inject' ]

	this.parse = function(parser, nodes) {

		const token = parser.nextToken()
		const args = parser.parseSignature(null, true)

		parser.advanceAfterBlockEnd(token.value)

		return new nodes.CallExtensionAsync(this, 'run', args)

	}

	this.run = function(context, filePath, data, next) {

		filePath = path.resolve(src, filePath)

		// Allow usage without data passed to the tag
		if (next == null) {
			next = data
			data = null
		}

		const opts = {
			prepend: '',
			append: '',
			src
		}

		render(filePath, data, opts, (err, str) => {

			if (err != null) return next(err)

			// Tell Nunjucks that it shouldn't escape the HTML of the component
			str = new njk.runtime.SafeString(str)

			next(null, str)

		})

	}

}

/**
 * Custom filter for Nunjucks. Replaces pipes with shy and
 * indicates that the string should not be auto escaped.
 * @param {?String} target - The target object the filter gets applied on.
 * @returns {*}
 */
const shyFilter = function(target) {

	if (target == null) return target

	target = target.replace(/\|/g, '')

	return new njk.runtime.SafeString(target)

}

/**
 * Creates a new Nunjucks enviroment.
 * @param {String} src - Injects of the inject tag are using this folder as their path base.
 * @returns {Object} New Nunjucks enviroment.
 */
const createEnvironment = function(src) {

	// Nunjucks (by default) uses the current working directory to look for files.
	// Paths in Nunjucks are always relative to the initial file.
	const loader = new njk.FileSystemLoader([ '/' ])

	const env = new njk.Environment(loader)

	env.addExtension('inject', new InjectTag(src))
	env.addFilter('shy', shyFilter)

	return env

}

/**
 * Renders a Nunjucks file with a new enviroment.
 * @param {String} filePath - Path to the Nunjucks file being rendered.
 * @param {Object} data - Nunjucks data used to render the file.
 * @param {Object} opts - Options.
 * @param {Function} next - The callback that handles the response. Receives the following properties: err, str.
 */
const render = function(filePath, data, opts, next) {

	const env = createEnvironment(opts.src)

	fs.readFile(filePath, 'utf8', (err, str) => {

		if (err != null) return next(err)

		str = opts.prepend + str + opts.append

		env.renderString(str, data, {
			path: filePath
		}, next)

	})

}

/**
 * Transforms Nunjucks to HTML.
 * @public
 * @param {?String} filePath - Path to the Nunjucks file being rendered.
 * @param {?Object} data - Nunjucks data used to render the file.
 * @param {?Object} opts - Options.
 * @returns {Promise<String>} HTML.
 */
module.exports = async function(filePath, data, opts) {

	const prepend = (opts != null && typeof opts.prepend === 'string') ? opts.prepend : ''
	const append = (opts != null && typeof opts.append === 'string') ? opts.append : ''
	const src = (opts != null && typeof opts.src === 'string') ? opts.src : process.cwd()

	return util.promisify(render)(filePath, data, {
		prepend,
		append,
		src
	})

}<commit_msg>Fix typos<commit_after>'use strict'

const util = require('util')
const path = require('path')
const fs = require('fs')
const njk = require('nunjucks')

/**
 * Custom tag for Nunjucks. Renders a file with custom data.
 * @param {String} src - Path base for further injects with the inject tag.
 */
const InjectTag = function(src) {

	this.tags = [ 'inject' ]

	this.parse = function(parser, nodes) {

		const token = parser.nextToken()
		const args = parser.parseSignature(null, true)

		parser.advanceAfterBlockEnd(token.value)

		return new nodes.CallExtensionAsync(this, 'run', args)

	}

	this.run = function(context, filePath, data, next) {

		filePath = path.resolve(src, filePath)

		// Allow usage without data passed to the tag
		if (next == null) {
			next = data
			data = null
		}

		const opts = {
			prepend: '',
			append: '',
			src
		}

		render(filePath, data, opts, (err, str) => {

			if (err != null) return next(err)

			// Tell Nunjucks that it shouldn't escape the HTML of the component
			str = new njk.runtime.SafeString(str)

			next(null, str)

		})

	}

}

/**
 * Custom filter for Nunjucks. Replaces pipes with shy and
 * indicates that the string should not be auto escaped.
 * @param {?String} target - The target object the filter gets applied on.
 * @returns {*}
 */
const shyFilter = function(target) {

	if (target == null) return target

	target = target.replace(/\|/g, '')

	return new njk.runtime.SafeString(target)

}

/**
 * Creates a new Nunjucks environment.
 * @param {String} src - Injects of the inject tag are using this folder as their path base.
 * @returns {Object} New Nunjucks environment.
 */
const createEnvironment = function(src) {

	// Nunjucks (by default) uses the current working directory to look for files.
	// Paths in Nunjucks are always relative to the initial file.
	const loader = new njk.FileSystemLoader([ '/' ])

	const env = new njk.Environment(loader)

	env.addExtension('inject', new InjectTag(src))
	env.addFilter('shy', shyFilter)

	return env

}

/**
 * Renders a Nunjucks file with a new environment.
 * @param {String} filePath - Path to the Nunjucks file being rendered.
 * @param {Object} data - Nunjucks data used to render the file.
 * @param {Object} opts - Options.
 * @param {Function} next - The callback that handles the response. Receives the following properties: err, str.
 */
const render = function(filePath, data, opts, next) {

	const env = createEnvironment(opts.src)

	fs.readFile(filePath, 'utf8', (err, str) => {

		if (err != null) return next(err)

		str = opts.prepend + str + opts.append

		env.renderString(str, data, {
			path: filePath
		}, next)

	})

}

/**
 * Transforms Nunjucks to HTML.
 * @public
 * @param {?String} filePath - Path to the Nunjucks file being rendered.
 * @param {?Object} data - Nunjucks data used to render the file.
 * @param {?Object} opts - Options.
 * @returns {Promise<String>} HTML.
 */
module.exports = async function(filePath, data, opts) {

	const prepend = (opts != null && typeof opts.prepend === 'string') ? opts.prepend : ''
	const append = (opts != null && typeof opts.append === 'string') ? opts.append : ''
	const src = (opts != null && typeof opts.src === 'string') ? opts.src : process.cwd()

	return util.promisify(render)(filePath, data, {
		prepend,
		append,
		src
	})

}<|endoftext|>


<commit_before>import { Component } from 'react'
import { observable, unobserve, observeLazy } from '@nx-js/observer-util'
import autoBind from './autoBind'

const REACTIVE_RENDER = Symbol('reactive render')
const DIRECT_RENDER = Symbol('direct render')

export default function easyComp (Comp) {
  if (typeof Comp !== 'function') {
    throw new TypeError('easyComp expects a component as argument.')
  }
  // wrap the component in a reactive HOC
  return toReactiveComp(Comp)
}

function toReactiveComp (Comp) {
  const isStatelessComp = !(Comp.prototype && Comp.prototype.isReactComponent)
  const BaseComp = isStatelessComp ? Component : Comp
  // return a HOC which overwrites render, shouldComponentUpdate and componentWillUnmount
  // it decides when to run the new reactive methods and when to proxy to the original methods
  class ReactiveHOC extends BaseComp {
    state = {
      renderIndicator: false
    }

    constructor (props, context) {
      super(props, context)

      if (!isStatelessComp) {
        // auto bind non react specific original methods to the component instance
        autoBind(this, Comp.prototype, true)

        // turn the store into an observable object, which triggers rendering on mutations
        if (typeof this.store === 'object' && this.store !== null) {
          this.store = observable(this.store)
        } else if (this.store !== undefined) {
          throw new TypeError('component.store must be an object or undefined')
        }
      }

      // create a reactive render for the component
      this[REACTIVE_RENDER] = observeLazy(this[REACTIVE_RENDER].bind(this))
    }

    render () {
      try {
        // indicate that render was called by React internals
        this[DIRECT_RENDER] = true
        return this[REACTIVE_RENDER]()
      } finally {
        // indicate that the direct render is over
        this[DIRECT_RENDER] = false
      }
    }

    [REACTIVE_RENDER] () {
      if (this[DIRECT_RENDER]) {
        // if render was called directly (by React or forceUpdate) get and save the next view
        return isStatelessComp ? Comp(this.props, this.context) : super.render()
      } else {
        // if render was called automatically because of store changes
        // trigger a react render (which results in a direct render later)
        this.setState({ renderIndicator: !this.state.renderIndicator })
      }
    }

    // react should trigger updates on prop changes, while easyState handles store changes
    shouldComponentUpdate (nextProps, nextState) {
      const { props, state } = this

      // respect the case when user prohibits updates
      if (super.shouldComponentUpdate && !super.shouldComponentUpdate(nextProps, nextState)) {
        return false
      }

      // return true if it is a reactive render
      if (state.renderIndicator !== nextState.renderIndicator) {
        return true
      }

      // shallow check if the props changed
      const keys = Object.keys(props)
      const nextKeys = Object.keys(nextProps)

      // component should update if the number of its props changed
      if (keys.length !== nextKeys.length) {
        return true
      }

      // component should update if any of its props changed value
      for (let key of keys) {
        if (props[key] !== nextProps[key]) {
          return true
        }
      }

      // do not let react update the comp otherwise, leave store triggered updates to easyState
      return false
    }

    componentWillUnmount () {
      // clean up memory used by easyState
      unobserve(this[REACTIVE_RENDER])

      // also call user defined componentWillUnmount to allow the user
      // to clean up additional memory
      if (super.componentWillUnmount) {
        super.componentWillUnmount()
      }
    }
  }
  // proxy react specific static variables to the reactive component
  copyStaticProps(Comp, ReactiveHOC)
  return ReactiveHOC
}

// copy react specific static props between passed and HOC components
function copyStaticProps (fromComp, toComp) {
  toComp.displayName = fromComp.displayName || fromComp.name
  toComp.contextTypes = fromComp.contextTypes
  toComp.childContextTypes = fromComp.childContextTypes
  toComp.propTypes = fromComp.propTypes
  toComp.defaultProps = fromComp.defaultProps
}
<commit_msg>refactor (easyComp): change to the new observe interface<commit_after>import { Component } from 'react'
import { observable, observe, unobserve } from '@nx-js/observer-util'
import autoBind from './autoBind'

const REACTIVE_RENDER = Symbol('reactive render')
const DIRECT_RENDER = Symbol('direct render')
const observeOptions = { lazy: true }

export default function easyComp (Comp) {
  if (typeof Comp !== 'function') {
    throw new TypeError('easyComp expects a component as argument.')
  }
  // wrap the component in a reactive HOC
  return toReactiveComp(Comp)
}

function toReactiveComp (Comp) {
  const isStatelessComp = !(Comp.prototype && Comp.prototype.isReactComponent)
  const BaseComp = isStatelessComp ? Component : Comp
  // return a HOC which overwrites render, shouldComponentUpdate and componentWillUnmount
  // it decides when to run the new reactive methods and when to proxy to the original methods
  class ReactiveHOC extends BaseComp {
    state = {
      renderIndicator: false
    }

    constructor (props, context) {
      super(props, context)

      if (!isStatelessComp) {
        // auto bind non react specific original methods to the component instance
        autoBind(this, Comp.prototype, true)

        // turn the store into an observable object, which triggers rendering on mutations
        if (typeof this.store === 'object' && this.store !== null) {
          this.store = observable(this.store)
        } else if (this.store !== undefined) {
          throw new TypeError('component.store must be an object or undefined')
        }
      }

      // create a reactive render for the component
      this[REACTIVE_RENDER] = observe(this[REACTIVE_RENDER].bind(this), observeOptions)
    }

    render () {
      try {
        // indicate that render was called by React internals
        this[DIRECT_RENDER] = true
        return this[REACTIVE_RENDER]()
      } finally {
        // indicate that the direct render is over
        this[DIRECT_RENDER] = false
      }
    }

    [REACTIVE_RENDER] () {
      if (this[DIRECT_RENDER]) {
        // if render was called directly (by React or forceUpdate) get and save the next view
        return isStatelessComp ? Comp(this.props, this.context) : super.render()
      } else {
        // if render was called automatically because of store changes
        // trigger a react render (which results in a direct render later)
        this.setState({ renderIndicator: !this.state.renderIndicator })
      }
    }

    // react should trigger updates on prop changes, while easyState handles store changes
    shouldComponentUpdate (nextProps, nextState) {
      const { props, state } = this

      // respect the case when user prohibits updates
      if (super.shouldComponentUpdate && !super.shouldComponentUpdate(nextProps, nextState)) {
        return false
      }

      // return true if it is a reactive render
      if (state.renderIndicator !== nextState.renderIndicator) {
        return true
      }

      // shallow check if the props changed
      const keys = Object.keys(props)
      const nextKeys = Object.keys(nextProps)

      // component should update if the number of its props changed
      if (keys.length !== nextKeys.length) {
        return true
      }

      // component should update if any of its props changed value
      for (let key of keys) {
        if (props[key] !== nextProps[key]) {
          return true
        }
      }

      // do not let react update the comp otherwise, leave store triggered updates to easyState
      return false
    }

    componentWillUnmount () {
      // clean up memory used by easyState
      unobserve(this[REACTIVE_RENDER])

      // also call user defined componentWillUnmount to allow the user
      // to clean up additional memory
      if (super.componentWillUnmount) {
        super.componentWillUnmount()
      }
    }
  }
  // proxy react specific static variables to the reactive component
  copyStaticProps(Comp, ReactiveHOC)
  return ReactiveHOC
}

// copy react specific static props between passed and HOC components
function copyStaticProps (fromComp, toComp) {
  toComp.displayName = fromComp.displayName || fromComp.name
  toComp.contextTypes = fromComp.contextTypes
  toComp.childContextTypes = fromComp.childContextTypes
  toComp.propTypes = fromComp.propTypes
  toComp.defaultProps = fromComp.defaultProps
}
<|endoftext|>


<commit_before>var
	fs = require("fs"),
	path = require("path"),
	walker = require("walker"),
	jsp = require("uglify-js").parser,
	pro = require("uglify-js").uglify,
	nopt = require("nopt"),
	less = require("less")
	;

var basename = path.basename(__filename),
    w = console.log,
    e = console.error;

// Shimming path.relative with 0.8.8's version if it doesn't exist
if(!path.relative){
  path.relative = require('./path-relative-shim').relative;
}

function printUsage() {
	w("Enyo 2.0 Minifier");
	w("Flags:");
	w("-no-less:", "Don't compile less; instad substitute css for less");
	w("-no-alias:", "Don't use path macros");
	w("-alias:", "Give paths a macroized alias");
	w("-enyo ENYOPATH:", "Path to enyo loader (enyo/enyo.js)");
	w("-output PATH/NAME:", "name of output file, prepend folder paths to change output directory");
	w("-h, -?, -help:", "Show this message");
}

// properly split path based on platform
function pathSplit(inPath) {
	var sep = process.platform == "win32" ? "\\" : "/";
	return inPath.split(sep);
}

buildPathBlock = function(loader) {
	var p$ = [];
	for (var i=0, p; (p=loader.packages[i]); i++) {
		if (p.name.indexOf("-") == -1) {
			p$.push(p.name + ': "' + p.folder + '"');
		}
	}
	p = p$.join(', ');
	return !p ? "" : "\n// minifier: path aliases\n\nenyo.path.addPaths({" + p + "});\n";
};

concatCss = function(loader, doneCB) {
	w("");
	var blob = "";
	var addToBlob = function(sheet, code) {
		// fix url paths
		code = code.replace(/url\([^)]*\)/g, function(inMatch) {
			// find the url path, ignore quotes in url string
			var matches = /url\s*\(\s*(('([^']*)')|("([^"]*)")|([^'"]*))\s*\)/.exec(inMatch);
			var urlPath = matches[3] || matches[5] || matches[6];
			// skip data urls
			if (/^data:/.test(urlPath)) {
				return "url(" + urlPath + ")";
			}
			// get absolute path to referenced asset
			var normalizedUrlPath = path.join(sheet, "..", urlPath);
			// Make relative asset path to built css
			var relPath = path.relative(path.dirname(opt.output || "build"), normalizedUrlPath);
			if (process.platform == "win32") {
				relPath = pathSplit(relPath).join("/");
			}
			return "url(" + relPath + ")";
		});
		blob += "\n/* " + sheet + " */\n\n" + code + "\n";
	}
	// Pops one sheet off the sheets[] array, reads (and parses if less), and then
	// recurses again from the async callback until no sheets left, then calls doneCB
	var readAndParse = function(sheets) {
		var sheet = sheets.shift();
		if (sheet) {
			w(sheet);
			var isLess = (sheet.slice(-4) == "less");
			if (isLess && (opt.less !== undefined)) {
				sheet = sheet.slice(0, sheet.length-4) + "css";
				isLess = false;
				w(" (Substituting CSS: " + sheet + ")");
			}
			var code = fs.readFileSync(sheet, "utf8");
			if (isLess) {
				var parser = new(less.Parser)({filename:sheet, paths:[path.dirname(sheet)]});
				parser.parse(code, function (err, tree) {
					if (err) {
						console.error(err);
					} else {
						addToBlob(sheet, tree.toCSS());
					}
					readAndParse(sheets);
				});
			} else {
				addToBlob(sheet, code);
				readAndParse(sheets);
			}
		} else {
			doneCB(blob);
		}
	}
	readAndParse(loader.sheets);
};

concatJs = function(loader) {
	w("");
	var blob = "";
	for (var i=0, m; (m=loader.modules[i]); i++) {
		if (typeof opt.alias === 'undefined' || opt.alias) {
			w("* inserting path aliases");
			blob += buildPathBlock(loader);
			opt.alias = false;
		}
		w(m.path);
		blob += "\n// " + m.rawPath + "\n\n" + compressJsFile(m.path) + "\n";
		if (opt.alias == m.rawPath) {
			w("* inserting path aliases");
			blob += buildPathBlock(loader);
		}
	}
	return blob;
};

compress = function(inCode) {
	var ast = jsp.parse(inCode); // parse code and get the initial AST
	ast = pro.ast_mangle(ast); // get a new AST with mangled names
	ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
	return pro.gen_code(ast, {indent_level:0, beautify: !opt.aggro, ascii_only:true}); // compressed code here
};

compressJsFile = function(inPath) {
	var code = fs.readFileSync(inPath, "utf8");
	return compress(code);
};

finish = function(loader) {
	//w(loader.packages);
	//w('');
	//
	var output = opt.output || "build";
	var outfolder = path.dirname(output);
	var exists = fs.existsSync || path.existsSync;
	if (outfolder != "." && !exists(outfolder)) {
		fs.mkdirSync(outfolder);
	}
	// Unfortunately, less parsing is asynchronous, so concatCSS is now as well
	concatCss(loader, function(css) {
		if (css.length) {
			w("");
			fs.writeFileSync(output + ".css", css, "utf8");
		}
		//
		var js = concatJs(loader);
		/*
		if (css.length) {
			js += "\n// " + "minifier: load css" + "\n\n";
			js += 'enyo.machine.sheet("' + output + '.css");\n';
		}
		*/
		if (js.length) {
			w("");
			fs.writeFileSync(output + ".js", js, "utf8");
		}
		//
		w("");
		w("done.");
		w("");

		// required to properly terminate a
		// node.process.fork() call, as defined by
		// <http://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options>
		process.exit(0);
	});
};

var knownOpts = {
  "alias": Boolean,
  "enyo": String,
  "output": String,
  "help": Boolean
};

var shortHands = {
  "alias": ['--alias'],
  "enyo": ['--enyo'],
  "output": ['--output'],
  "h": ['--help'],
  "?": ['--help'],
  "help": ['--help']
};

opt = nopt(knownOpts, shortHands, process.argv, 2);
opt.source = opt.argv.remain[0];
w(opt);
w("");

w("");

if (opt.help) {
	printUsage();
	process.exit();
}

// Send message to parent node process, if any
process.on('uncaughtException', function (err) {
	e(err.stack);
	if (process.send) {
		// only available if parent-process is node
		process.send({error: err});
	}
	process.exit(1);
});
// receive error messages from child node processes
process.on('message', function(msg) {
	console.dir(basename, msg);
	if (msg.error && msg.error.stack) {
		console.error(basename, msg.error.stack);
	}
	if (process.send) {
		process.send(msg);
	}
});

walker.init(opt.enyo);
walker.walk(opt.source, finish);

<commit_msg>ENYO-1514: minifier will not force relative path for external links in url statements<commit_after>var
	fs = require("fs"),
	path = require("path"),
	walker = require("walker"),
	jsp = require("uglify-js").parser,
	pro = require("uglify-js").uglify,
	nopt = require("nopt"),
	less = require("less")
	;

var basename = path.basename(__filename),
    w = console.log,
    e = console.error;

// Shimming path.relative with 0.8.8's version if it doesn't exist
if(!path.relative){
  path.relative = require('./path-relative-shim').relative;
}

function printUsage() {
	w("Enyo 2.0 Minifier");
	w("Flags:");
	w("-no-less:", "Don't compile less; instad substitute css for less");
	w("-no-alias:", "Don't use path macros");
	w("-alias:", "Give paths a macroized alias");
	w("-enyo ENYOPATH:", "Path to enyo loader (enyo/enyo.js)");
	w("-output PATH/NAME:", "name of output file, prepend folder paths to change output directory");
	w("-h, -?, -help:", "Show this message");
}

// properly split path based on platform
function pathSplit(inPath) {
	var sep = process.platform == "win32" ? "\\" : "/";
	return inPath.split(sep);
}

buildPathBlock = function(loader) {
	var p$ = [];
	for (var i=0, p; (p=loader.packages[i]); i++) {
		if (p.name.indexOf("-") == -1) {
			p$.push(p.name + ': "' + p.folder + '"');
		}
	}
	p = p$.join(', ');
	return !p ? "" : "\n// minifier: path aliases\n\nenyo.path.addPaths({" + p + "});\n";
};

concatCss = function(loader, doneCB) {
	w("");
	var blob = "";
	var addToBlob = function(sheet, code) {
		// fix url paths
		code = code.replace(/url\([^)]*\)/g, function(inMatch) {
			// find the url path, ignore quotes in url string
			var matches = /url\s*\(\s*(('([^']*)')|("([^"]*)")|([^'"]*))\s*\)/.exec(inMatch);
			var urlPath = matches[3] || matches[5] || matches[6];
			// skip data urls
			if (/^data:/.test(urlPath)) {
				return "url(" + urlPath + ")";
			}
			// skip an external link
			if (/^http(:?s)?:/.test(urlPath)) {
			    return "url(" + urlPath + ")";
			}
			// get absolute path to referenced asset
			var normalizedUrlPath = path.join(sheet, "..", urlPath);
			// Make relative asset path to built css
			var relPath = path.relative(path.dirname(opt.output || "build"), normalizedUrlPath);
			if (process.platform == "win32") {
				relPath = pathSplit(relPath).join("/");
			}
			return "url(" + relPath + ")";
		});
		blob += "\n/* " + sheet + " */\n\n" + code + "\n";
	}
	// Pops one sheet off the sheets[] array, reads (and parses if less), and then
	// recurses again from the async callback until no sheets left, then calls doneCB
	var readAndParse = function(sheets) {
		var sheet = sheets.shift();
		if (sheet) {
			w(sheet);
			var isLess = (sheet.slice(-4) == "less");
			if (isLess && (opt.less !== undefined)) {
				sheet = sheet.slice(0, sheet.length-4) + "css";
				isLess = false;
				w(" (Substituting CSS: " + sheet + ")");
			}
			var code = fs.readFileSync(sheet, "utf8");
			if (isLess) {
				var parser = new(less.Parser)({filename:sheet, paths:[path.dirname(sheet)]});
				parser.parse(code, function (err, tree) {
					if (err) {
						console.error(err);
					} else {
						addToBlob(sheet, tree.toCSS());
					}
					readAndParse(sheets);
				});
			} else {
				addToBlob(sheet, code);
				readAndParse(sheets);
			}
		} else {
			doneCB(blob);
		}
	}
	readAndParse(loader.sheets);
};

concatJs = function(loader) {
	w("");
	var blob = "";
	for (var i=0, m; (m=loader.modules[i]); i++) {
		if (typeof opt.alias === 'undefined' || opt.alias) {
			w("* inserting path aliases");
			blob += buildPathBlock(loader);
			opt.alias = false;
		}
		w(m.path);
		blob += "\n// " + m.rawPath + "\n\n" + compressJsFile(m.path) + "\n";
		if (opt.alias == m.rawPath) {
			w("* inserting path aliases");
			blob += buildPathBlock(loader);
		}
	}
	return blob;
};

compress = function(inCode) {
	var ast = jsp.parse(inCode); // parse code and get the initial AST
	ast = pro.ast_mangle(ast); // get a new AST with mangled names
	ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
	return pro.gen_code(ast, {indent_level:0, beautify: !opt.aggro, ascii_only:true}); // compressed code here
};

compressJsFile = function(inPath) {
	var code = fs.readFileSync(inPath, "utf8");
	return compress(code);
};

finish = function(loader) {
	//w(loader.packages);
	//w('');
	//
	var output = opt.output || "build";
	var outfolder = path.dirname(output);
	var exists = fs.existsSync || path.existsSync;
	if (outfolder != "." && !exists(outfolder)) {
		fs.mkdirSync(outfolder);
	}
	// Unfortunately, less parsing is asynchronous, so concatCSS is now as well
	concatCss(loader, function(css) {
		if (css.length) {
			w("");
			fs.writeFileSync(output + ".css", css, "utf8");
		}
		//
		var js = concatJs(loader);
		/*
		if (css.length) {
			js += "\n// " + "minifier: load css" + "\n\n";
			js += 'enyo.machine.sheet("' + output + '.css");\n';
		}
		*/
		if (js.length) {
			w("");
			fs.writeFileSync(output + ".js", js, "utf8");
		}
		//
		w("");
		w("done.");
		w("");

		// required to properly terminate a
		// node.process.fork() call, as defined by
		// <http://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options>
		process.exit(0);
	});
};

var knownOpts = {
  "alias": Boolean,
  "enyo": String,
  "output": String,
  "help": Boolean
};

var shortHands = {
  "alias": ['--alias'],
  "enyo": ['--enyo'],
  "output": ['--output'],
  "h": ['--help'],
  "?": ['--help'],
  "help": ['--help']
};

opt = nopt(knownOpts, shortHands, process.argv, 2);
opt.source = opt.argv.remain[0];
w(opt);
w("");

w("");

if (opt.help) {
	printUsage();
	process.exit();
}

// Send message to parent node process, if any
process.on('uncaughtException', function (err) {
	e(err.stack);
	if (process.send) {
		// only available if parent-process is node
		process.send({error: err});
	}
	process.exit(1);
});
// receive error messages from child node processes
process.on('message', function(msg) {
	console.dir(basename, msg);
	if (msg.error && msg.error.stack) {
		console.error(basename, msg.error.stack);
	}
	if (process.send) {
		process.send(msg);
	}
});

walker.init(opt.enyo);
walker.walk(opt.source, finish);

<|endoftext|>


<commit_before>/* npm.js */

var Q = require("q");
var NPM = require("npm");

var npm = module.exports = {};

/* npm.load */
npm.load = Q.nbind(NPM.load, NPM);

/* npm.commands */
npm.commands = {};
['update', 'install'].forEach(function(key) {
	npm.commands[key] = Q.nbind(NPM.commands[key], NPM);
});

/* npm.on */
npm.on = NPM.on.bind(NPM);

/* EOF */
<commit_msg>Updated<commit_after>/* npm.js */

var Q = require("q");
var NPM = require("npm");

var npm = module.exports = {};

/* npm.load */
npm.load = Q.nbind(NPM.load, NPM);

/* npm.commands */
npm.commands = {};
['run', 'update', 'install'].forEach(function(key) {
	npm.commands[key] = Q.nbind(NPM.commands[key], NPM);
});

/* npm.on */
npm.on = NPM.on.bind(NPM);

/* EOF */
<|endoftext|>


<commit_before>const Buffer = require('safe-buffer').Buffer
const { bufferUInt64 } = require('./buffer-build')
const hashFromBuffer = require('./tx-decoder').getTxId

// pow :: Buffer -> Number -> NonceNumber
const pow = difficulty => (buffer, nonce = 0) => {
  let hash
  let now = Date.now()
  const check = [...Array(difficulty)].reduce(acc => acc + '0', '')
  nonce--
  console.log(`Starting PoW for ${difficulty}:`)
  do {
    nonce++
    hash = hashFromBuffer(Buffer.concat([buffer, bufferUInt64(nonce)]))
    if (nonce % 500000 === 0) {
      console.log(`- ${Math.floor((Date.now() - now) / 1000)}, nonce = ${nonce}, hash = ${hash}`)
    }
  } while (hash.slice(0, difficulty) !== check)
  console.log(`Finished with: time = ${Math.floor((Date.now() - now) / 1000)}, nonce = ${nonce}, hash = ${hash}`)
  return nonce
}

module.exports = pow
<commit_msg>minor debug<commit_after>const Buffer = require('safe-buffer').Buffer
const { bufferUInt64 } = require('./buffer-build')
const hashFromBuffer = require('./tx-decoder').getTxId

// pow :: Buffer -> Number -> NonceNumber
const pow = difficulty => (buffer, nonce = 0) => {
  let hash
  let now = Date.now()
  const check = [...Array(difficulty)].reduce(acc => acc + '0', '')
  nonce--
  console.log(`Starting PoW for ${difficulty}:`)
  do {
    nonce++
    hash = hashFromBuffer(Buffer.concat([buffer, bufferUInt64(nonce)]))
    if (nonce % 5000 * 1000 === 0) {  // (5 * 10^6) takes ~30sec
      console.log(`- ${Math.floor((Date.now() - now) / 1000)}, nonce = ${nonce}, hash = ${hash}`)
    }
  } while (hash.slice(0, difficulty) !== check)
  console.log(`Finished with: time = ${Math.floor((Date.now() - now) / 1000)}, nonce = ${nonce}, hash = ${hash}`)
  return nonce
}

module.exports = pow
<|endoftext|>