<commit_before>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        website: false,
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false,
        'default-branch': 'master'
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, generateChangelog } = require('./lib/git');
const askDeploy = require('./lib/ask');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        {
            title: 'Check if the deploy branch exists',
            skip() {
                if (!(process.env.QA_BRANCHES || '').length) {
                    return 'If you do not have QA_BRANCHES inside your .env you cannot deploy to something new';
                }

                if (`deploy-${process.env.QA_BRANCH}` === branch) {
                    return '🤖 No need to check for this branch, it exists';
                }

                const branches = process.env.QA_BRANCHES.split(',').join('|');
                // Do not try to deploy on QA or cobalt
                if (new RegExp(`deploy-(cobalt|${branches})`).test(branch)) {
                    return '✋ You shall not deploy to QA';
                }
            },
            enabled: () => !isCI && !/dev|beta|prod|tor|old/.test(branch),
            async task() {
                // For the CI to force SSH
                if (process.env.GIT_REMOTE_URL_CI && argv.fromCi) {
                    await bash(`git remote set-url origin ${process.env.GIT_REMOTE_URL_CI}`);
                }
                return script('createNewDeployBranch.sh', ['--check', branch.replace('deploy-', '')], 'inherit');
            }
        },
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && (await bash('mkdir dist'));
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
                debug(ctx, 'git env bundle');
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: 'Generate the version info',
            task(ctx) {
                const { tag = `v${PKG.version}`, originCommit } = ctx || {};
                const fileName = path.join('dist', 'assets/version.json');

                if (/v4/.test(branch)) {
                    const date = moment().format('MM.DD');
                    return script('createVersionJSON.sh', [originCommit, `v4.0.0~${date}`, fileName]);
                }

                return script('createVersionJSON.sh', [originCommit, tag, fileName]);
            }
        },
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'bundle-only';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        isCI,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return askDeploy(branch, PKG, argv);
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const parseEnv = ({ branch, website }) => {
        if (website && /deploy-(a|b)$/.test(branch)) {
            return 'deploy-prod';
        }
        return branch;
    };

    debug(argv, 'arguments');
    const branchName = parseEnv(argv);
    return askDeploy(branchName, PKG, argv);
}

if (argv._.includes('changelog')) {
    const { branch, mode, api } = argv;
    const url = argv.url || (PKG.bugs || {}).url;
    const isV4 = mode === 'v4';
    debug({ argv }, 'arguments');

    if (!['dev', 'v4', process.env.QA_BRANCH].includes(branch) && !isV4) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    if (!url && !isV4) {
        return warn('No URL found for the issues');
    }

    return generateChangelog(branch, url, isV4).then((data) => {
        const env = isV4 ? 'https://v4.protonmail.blue' : branch;
        if (data) {
            coucou.send(data, { env, mode: 'changelog', api }, PKG);
        }
    });
}

main().catch(error);
<commit_msg>Dir i18n is missing<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const Listr = require('listr');
const execa = require('execa');
const chalk = require('chalk');
const del = require('del');
const UpdaterRenderer = require('listr-update-renderer');
const moment = require('moment');
const argv = require('minimist')(process.argv.slice(2), {
    string: ['appMode'],
    boolean: ['lint', 'i18n'],
    default: {
        website: false,
        lint: true,
        fromCi: false,
        i18n: true,
        localize: false,
        appMode: 'bundle',
        remote: false,
        forceFetch: false,
        silentMessage: false,
        'default-branch': 'master'
    }
});

// Compat mode WebClient
const ENV_FILE = fs.existsSync('.env') ? '.env' : 'env/.env';
require('dotenv').config({ path: ENV_FILE });

const { debug, success, error, about, warn } = require('./lib/helpers/log')('proton-bundler');
const coucou = require('./lib/helpers/coucou');
const { bash, script } = require('./lib/helpers/cli');
const {
    customBundler: { tasks: customTasks, config: customConfig },
    getCustomHooks
} = require('./lib/custom');
const { pull, push, getConfig, generateChangelog } = require('./lib/git');
const askDeploy = require('./lib/ask');
const buildRemote = require('./lib/buildRemote');

const PKG = require(path.join(process.cwd(), 'package.json'));

const getTasks = (branch, { isCI, flowType = 'single', forceI18n, appMode, runI18n, isRemoteBuild }) => {
    const { EXTERNAL_FILES = ['.htaccess'] } = customConfig;
    const { hookPreTasks, hookPostTasks, hookPostTaskClone, hookPostTaskBuild, customConfigSetup } = getCustomHooks(
        customTasks({
            branch,
            isCI,
            flowType,
            appMode,
            forceI18n,
            runI18n,
            isRemoteBuild
        })
    );

    const configTasks = customConfigSetup.length
        ? customConfigSetup
        : [
              {
                  title: 'Setup app config',
                  enabled: () => !isCI,
                  task() {
                      return bash('npx proton-pack', process.argv.slice(2));
                  }
              }
          ];

    const list = [
        {
            title: 'Check if the deploy branch exists',
            skip() {
                if (!(process.env.QA_BRANCHES || '').length) {
                    return 'If you do not have QA_BRANCHES inside your .env you cannot deploy to something new';
                }

                if (`deploy-${process.env.QA_BRANCH}` === branch) {
                    return '🤖 No need to check for this branch, it exists';
                }

                const branches = process.env.QA_BRANCHES.split(',').join('|');
                // Do not try to deploy on QA or cobalt
                if (new RegExp(`deploy-(cobalt|${branches})`).test(branch)) {
                    return '✋ You shall not deploy to QA';
                }
            },
            enabled: () => !isCI && !/dev|beta|prod|tor|old/.test(branch),
            async task() {
                // For the CI to force SSH
                if (process.env.GIT_REMOTE_URL_CI && argv.fromCi) {
                    await bash(`git remote set-url origin ${process.env.GIT_REMOTE_URL_CI}`);
                }
                return script('createNewDeployBranch.sh', ['--check', branch.replace('deploy-', '')], 'inherit');
            }
        },
        ...hookPreTasks,
        {
            title: 'Save dependencies if we need',
            enabled: () => !isRemoteBuild && !isCI && /dev|beta|alpha/.test(branch),
            task() {
                return script('updatePackageLock.sh', [argv['default-branch']]);
            }
        },
        {
            title: 'Clear previous dist',
            async task() {
                await del(['dist', 'distCurrent', 'distback'], { dryRun: false });
                isCI && (await bash('mkdir dist'));
            }
        },
        {
            title: 'Prepare build missing dir',
            async task() {
                await bash("[ ! -d 'src/i18n' ] && mkdir src/i18n");
            }
        },
        {
            title: 'Lint sources',
            enabled: () => argv.lint !== false && !isRemoteBuild,
            task: () => execa('npm', ['run', 'lint'])
        },
        ...configTasks,
        {
            title: 'Extract git env for the bundle',
            async task(ctx) {
                const { commit, branch, tag } = await getConfig();
                ctx.originCommit = commit;
                ctx.originBranch = branch;
                ctx.tag = tag;
                debug(ctx, 'git env bundle');
            }
        },
        {
            title: `Pull dist branch ${branch}`,
            enabled: () => !isCI,
            task: () => pull(branch, argv.forceFetch, argv.fromCi)
        },
        ...hookPostTaskClone,
        {
            title: 'Copy some files',
            task() {
                const rule = EXTERNAL_FILES.length > 1 ? `{${EXTERNAL_FILES.join(',')}}` : EXTERNAL_FILES.join(',');
                return bash(`cp src/${rule} dist/`);
            }
        },
        {
            title: 'Upgrade translations inside the app',
            enabled: () => forceI18n || (runI18n && !isCI && /prod|beta/.test(branch)),
            task() {
                return execa('npm', ['run', 'i18n:getlatest']);
            }
        },
        {
            title: 'Build the application',
            task() {
                const args = process.argv.slice(2);
                if (appMode === 'standalone') {
                    return execa('npm', ['run', 'build:standalone', '--', ...args]);
                }

                return execa('npm', ['run', 'build', '--', ...args]);
            }
        },
        ...hookPostTaskBuild,
        {
            title: 'Generate the version info',
            task(ctx) {
                const { tag = `v${PKG.version}`, originCommit } = ctx || {};
                const fileName = path.join('dist', 'assets/version.json');

                if (/v4/.test(branch)) {
                    const date = moment().format('MM.DD');
                    return script('createVersionJSON.sh', [originCommit, `v4.0.0~${date}`, fileName]);
                }

                return script('createVersionJSON.sh', [originCommit, tag, fileName]);
            }
        },
        {
            title: `Push dist to ${branch}`,
            enabled: () => !isCI,
            task: (ctx) => push(branch, ctx)
        },
        {
            title: 'Update crowdin with latest translations',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                return execa('npm', ['run', 'i18n:upgrade']);
            }
        },
        ...hookPostTasks,
        {
            title: 'Inform us about new translations available',
            enabled: () => runI18n && !isCI && /prod|beta/.test(branch),
            task() {
                const [, type] = branch.match(/deploy-(\w+)/) || [];
                return execa('npx', ['proton-i18n', 'coucou', type]);
            }
        }
    ];
    return list;
};

/**
 * Get the API dest when we deploy.
 * You can use the custom config from proton.bundler.js to get it
 * @return {Promise<String>}
 */
async function getAPIUrl() {
    if (customConfig.apiUrl) {
        return customConfig.apiUrl;
    }

    const args = process.argv.slice(2);
    const { stdout } = await bash('npx proton-pack print-config', args);
    debug(stdout, 'print-config output');
    const [, url] = stdout.match(/apiUrl": "(.+)"(,*?)/);
    return url;
}

async function main() {
    /*
        If we build from the remote repository we need to:
            - clone the repository inside /tmp
            - install dependencies
            - run the deploy command again from this directory
        So let's put an end to the current deploy.
     */
    if (argv.remote) {
        await buildRemote(PKG);
        const args = process.argv.slice(2).filter((key) => !/--remote/.test(key));
        return script('builder.sh', [PKG.name, ...args], 'inherit'); // inherit for the colors ;)
    }

    // Custom local deploy for the CI
    const isCI = process.env.NODE_ENV_DIST === 'bundle-only';
    const branch = argv.branch;
    const flowType = argv.flow;
    const runI18n = argv.i18n;
    const forceI18n = argv.localize;
    const appMode = argv.appMode;
    const isRemoteBuild = argv.source === 'remote';

    debug({ customConfig, argv }, 'configuration deploy');

    if (!branch && !isCI) {
        throw new Error('You must define a branch name. --branch=XXX');
    }

    const apiUrl = await getAPIUrl();

    process.env.NODE_ENV_BRANCH = branch;
    process.env.NODE_ENV_API = apiUrl;

    about({
        ...(!isCI && { branch }),
        apiUrl,
        appMode,
        isRemoteBuild,
        isCI,
        SENTRY: process.env.NODE_ENV_SENTRY
    });

    const start = moment(Date.now());
    const listTasks = getTasks(branch, { isCI, flowType, forceI18n, appMode, runI18n, isRemoteBuild });
    const tasks = new Listr(listTasks, {
        renderer: UpdaterRenderer,
        collapse: false
    });

    await tasks.run();

    const now = moment(Date.now());
    const total = now.diff(start, 'seconds');
    const time = total > 60 ? moment.utc(total * 1000).format('mm:ss') : `${total}s`;

    !isCI && success('App deployment done', { time });
    isCI && success(`Build CI app to the directory: ${chalk.bold('dist')}`, { time });

    if (!isCI && !argv.silentMessage) {
        return askDeploy(branch, PKG, argv);
    }
}

if (argv._.includes('hosts')) {
    return script('createNewDeployBranch.sh', process.argv.slice(3)).then(({ stdout }) => console.log(stdout));
}

if (argv._.includes('log-commits')) {
    const parseEnv = ({ branch, website }) => {
        if (website && /deploy-(a|b)$/.test(branch)) {
            return 'deploy-prod';
        }
        return branch;
    };

    debug(argv, 'arguments');
    const branchName = parseEnv(argv);
    return askDeploy(branchName, PKG, argv);
}

if (argv._.includes('changelog')) {
    const { branch, mode, api } = argv;
    const url = argv.url || (PKG.bugs || {}).url;
    const isV4 = mode === 'v4';
    debug({ argv }, 'arguments');

    if (!['dev', 'v4', process.env.QA_BRANCH].includes(branch) && !isV4) {
        return warn(`No changelog available for the branch ${branch}`); // not available
    }

    if (!url && !isV4) {
        return warn('No URL found for the issues');
    }

    return generateChangelog(branch, url, isV4).then((data) => {
        const env = isV4 ? 'https://v4.protonmail.blue' : branch;
        if (data) {
            coucou.send(data, { env, mode: 'changelog', api }, PKG);
        }
    });
}

main().catch(error);
<|endoftext|>


<commit_before>const Module = require('module')
const origRequire = Module._load

Module._load = function (moduleName, from) {
  console.log('-- requiring module: ', moduleName + ' from ' + from.filename)

  return origRequire.apply(this, arguments)
}

console.log('about to start requiring')
const {app} = require('electron')
console.log('done with app')
const {BrowserWindow} = require('electron')
console.log('done with BrowserWindow')
const nslog = require('nslog')
console.log('done with nslog')
const path = require('path')
console.log('done with path')
const temp = require('temp').track()
console.log('done with track')
const parseCommandLine = require('./parse-command-line')
console.log('done with parseCommandLine')
const startCrashReporter = require('../crash-reporter-start')
console.log('done with startCrashReporter')
const atomPaths = require('../atom-paths')
console.log('done with atomPaths')
const fs = require('fs')
console.log('done with fs')
const CSON = require('season')
console.log('done with CSON')
const Config = require('../config')

console.log('everything required correctly!')

module.exports = function start (resourcePath, devResourcePath, startTime) {
  global.shellStartTime = startTime

  process.on('uncaughtException', function (error = {}) {
    if (error.message != null) {
      console.log(error.message)
    }

    if (error.stack != null) {
      console.log(error.stack)
    }
  })

  process.on('unhandledRejection', function (error = {}) {
    if (error.message != null) {
      console.log(error.message)
    }

    if (error.stack != null) {
      console.log(error.stack)
    }
  })

  const previousConsoleLog = console.log
  console.log = nslog

  app.commandLine.appendSwitch('enable-experimental-web-platform-features')

  const args = parseCommandLine(process.argv.slice(1))
  args.resourcePath = normalizeDriveLetterName(resourcePath)
  args.devResourcePath = normalizeDriveLetterName(devResourcePath)

  atomPaths.setAtomHome(app.getPath('home'))
  atomPaths.setUserData(app)

  const config = getConfig()
  const colorProfile = config.get('core.colorProfile')
  if (colorProfile && colorProfile !== 'default') {
    app.commandLine.appendSwitch('force-color-profile', colorProfile)
  }

  if (handleStartupEventWithSquirrel()) {
    return
  } else if (args.test && args.mainProcess) {
    console.log('Running Atom main process tests...')
    app.setPath('userData', temp.mkdirSync('atom-user-data-dir-for-main-process-tests'))
    console.log = previousConsoleLog
    app.on('ready', function () {
      console.log('App ready!')
      const testRunner = require(path.join(args.resourcePath, 'spec/main-process/mocha-test-runner'))
      testRunner(args.pathsToOpen)
    })
    return
  }

  // NB: This prevents Win10 from showing dupe items in the taskbar
  app.setAppUserModelId('com.squirrel.atom.' + process.arch)

  function addPathToOpen (event, pathToOpen) {
    event.preventDefault()
    args.pathsToOpen.push(pathToOpen)
  }

  function addUrlToOpen (event, urlToOpen) {
    event.preventDefault()
    args.urlsToOpen.push(urlToOpen)
  }

  app.on('open-file', addPathToOpen)
  app.on('open-url', addUrlToOpen)
  app.on('will-finish-launching', startCrashReporter)

  if (args.userDataDir != null) {
    app.setPath('userData', args.userDataDir)
  } else if (args.test || args.benchmark || args.benchmarkTest) {
    app.setPath('userData', temp.mkdirSync('atom-test-data'))
  }

  app.on('ready', function () {
    app.removeListener('open-file', addPathToOpen)
    app.removeListener('open-url', addUrlToOpen)
    const AtomApplication = require(path.join(args.resourcePath, 'src', 'main-process', 'atom-application'))
    AtomApplication.open(args)
  })
}

function handleStartupEventWithSquirrel () {
  if (process.platform !== 'win32') {
    return false
  }

  const SquirrelUpdate = require('./squirrel-update')
  const squirrelCommand = process.argv[1]
  return SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
}

function getConfig () {
  const config = new Config()

  let configFilePath
  if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.json'))) {
    configFilePath = path.join(process.env.ATOM_HOME, 'config.json')
  } else if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.cson'))) {
    configFilePath = path.join(process.env.ATOM_HOME, 'config.cson')
  }

  if (configFilePath) {
    const configFileData = CSON.readFileSync(configFilePath)
    config.resetUserSettings(configFileData)
  }

  return config
}

function normalizeDriveLetterName (filePath) {
  if (process.platform === 'win32' && filePath) {
    return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':')
  } else {
    return filePath
  }
}
<commit_msg>Figure this exception out<commit_after>const Module = require('module')
const origRequire = Module._load

Module._load = function (moduleName, from) {
  console.log('-- requiring module: ', moduleName + ' from ' + from.filename)

  return origRequire.apply(this, arguments)
}

process.on('uncaughtException', error => {
  console.error(error.name + ': '+ error.message)
  console.error(error.stack)
})

console.log('about to start requiring')
const {app} = require('electron')
console.log('done with app')
const {BrowserWindow} = require('electron')
console.log('done with BrowserWindow')
const nslog = require('nslog')
console.log('done with nslog')
const path = require('path')
console.log('done with path')
const temp = require('temp').track()
console.log('done with track')
const parseCommandLine = require('./parse-command-line')
console.log('done with parseCommandLine')
const startCrashReporter = require('../crash-reporter-start')
console.log('done with startCrashReporter')
const atomPaths = require('../atom-paths')
console.log('done with atomPaths')
const fs = require('fs')
console.log('done with fs')
const CSON = require('season')
console.log('done with CSON')
const Config = require('../config')

console.log('everything required correctly!')

module.exports = function start (resourcePath, devResourcePath, startTime) {
  global.shellStartTime = startTime

  process.on('uncaughtException', function (error = {}) {
    if (error.message != null) {
      console.log(error.message)
    }

    if (error.stack != null) {
      console.log(error.stack)
    }
  })

  process.on('unhandledRejection', function (error = {}) {
    if (error.message != null) {
      console.log(error.message)
    }

    if (error.stack != null) {
      console.log(error.stack)
    }
  })

  const previousConsoleLog = console.log
  console.log = nslog

  app.commandLine.appendSwitch('enable-experimental-web-platform-features')

  const args = parseCommandLine(process.argv.slice(1))
  args.resourcePath = normalizeDriveLetterName(resourcePath)
  args.devResourcePath = normalizeDriveLetterName(devResourcePath)

  atomPaths.setAtomHome(app.getPath('home'))
  atomPaths.setUserData(app)

  const config = getConfig()
  const colorProfile = config.get('core.colorProfile')
  if (colorProfile && colorProfile !== 'default') {
    app.commandLine.appendSwitch('force-color-profile', colorProfile)
  }

  if (handleStartupEventWithSquirrel()) {
    return
  } else if (args.test && args.mainProcess) {
    console.log('Running Atom main process tests...')
    app.setPath('userData', temp.mkdirSync('atom-user-data-dir-for-main-process-tests'))
    console.log = previousConsoleLog
    app.on('ready', function () {
      console.log('App ready!')
      const testRunner = require(path.join(args.resourcePath, 'spec/main-process/mocha-test-runner'))
      testRunner(args.pathsToOpen)
    })
    return
  }

  // NB: This prevents Win10 from showing dupe items in the taskbar
  app.setAppUserModelId('com.squirrel.atom.' + process.arch)

  function addPathToOpen (event, pathToOpen) {
    event.preventDefault()
    args.pathsToOpen.push(pathToOpen)
  }

  function addUrlToOpen (event, urlToOpen) {
    event.preventDefault()
    args.urlsToOpen.push(urlToOpen)
  }

  app.on('open-file', addPathToOpen)
  app.on('open-url', addUrlToOpen)
  app.on('will-finish-launching', startCrashReporter)

  if (args.userDataDir != null) {
    app.setPath('userData', args.userDataDir)
  } else if (args.test || args.benchmark || args.benchmarkTest) {
    app.setPath('userData', temp.mkdirSync('atom-test-data'))
  }

  app.on('ready', function () {
    app.removeListener('open-file', addPathToOpen)
    app.removeListener('open-url', addUrlToOpen)
    const AtomApplication = require(path.join(args.resourcePath, 'src', 'main-process', 'atom-application'))
    AtomApplication.open(args)
  })
}

function handleStartupEventWithSquirrel () {
  if (process.platform !== 'win32') {
    return false
  }

  const SquirrelUpdate = require('./squirrel-update')
  const squirrelCommand = process.argv[1]
  return SquirrelUpdate.handleStartupEvent(app, squirrelCommand)
}

function getConfig () {
  const config = new Config()

  let configFilePath
  if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.json'))) {
    configFilePath = path.join(process.env.ATOM_HOME, 'config.json')
  } else if (fs.existsSync(path.join(process.env.ATOM_HOME, 'config.cson'))) {
    configFilePath = path.join(process.env.ATOM_HOME, 'config.cson')
  }

  if (configFilePath) {
    const configFileData = CSON.readFileSync(configFilePath)
    config.resetUserSettings(configFileData)
  }

  return config
}

function normalizeDriveLetterName (filePath) {
  if (process.platform === 'win32' && filePath) {
    return filePath.replace(/^([a-z]):/, ([driveLetter]) => driveLetter.toUpperCase() + ':')
  } else {
    return filePath
  }
}
<|endoftext|>


<commit_before>import createROIMap from './createROIMap';
import createROI from './createROI';
import extendObject from 'extend';

export default class ROIManager {

    constructor(image, options = {}) {
        this._image = image;
        this._options = options;
        this._layers = {};
        this._painted = null;
    }

    putMask(mask, maskLabel = 'default', options = {}) {
        let opt = extendObject({}, this._options, options);
        this._layers[maskLabel] = new ROILayer(mask, opt);
    }

    getROIMap(maskLabel = 'default') {
        if (!this._layers[maskLabel]) return;
        return this._layers[maskLabel].roiMap;
    }

    getROIIDs(options) {
        let rois = this.getROI(options);
        if (!rois) return;
        let ids = new Array(rois.length);
        for (let i = 0; i < rois.length; i++) {
            ids[i] = rois[i].id;
        }
        return ids;
    }

    getROI(maskLabel = 'default', {
        positive = true,
        negative = true,
        minSurface = 0,
        maxSurface = Number.POSITIVE_INFINITY
        } = {}) {
        let allROIs = this._layers[maskLabel].roi;
        let rois = new Array(allROIs.length);
        let ptr = 0;
        for (let i = 0; i < allROIs.length; i++) {
            let roi = allROIs[i];
            if (((roi.id < 0 && negative) || roi.id > 0 && positive)
                && roi.surface > minSurface
                && roi.surface < maxSurface) {
                rois[ptr++] = roi;
            }
        }
        rois.length = ptr;
        return rois;
    }

    getROIMasks(maskLabel = 'default', options = {}) {
        let rois = this.getROI(maskLabel, options);
        let masks = new Array(rois.length);
        for (let i = 0; i < rois.length; i++) {
            masks[i] = rois[i].mask;
        }
        return masks;
    }

    getPixels(maskLabel = 'default', options = {}) {
        if (this._layers[maskLabel]) {
            return this._layers[maskLabel].roiMap.pixels;
        }
    }

    paint(maskLabel = 'default', options = {}) {
        if (!this._painted) this._painted = this._image.clone();
        let masks = this.getROIMasks(maskLabel, options);
        this._painted.paintMasks(masks, options);
        return this._painted;
    }

    resetPainted() {
        this._painted = undefined;
    }

}

class ROILayer {
    constructor(mask, options) {
        this.mask = mask;
        this.options = options;
        this.roiMap = createROIMap(this.mask, options);
        this.roi = createROI(this.roiMap);
    }
}
<commit_msg>Add layer in getROIIDs<commit_after>import createROIMap from './createROIMap';
import createROI from './createROI';
import extendObject from 'extend';

export default class ROIManager {

    constructor(image, options = {}) {
        this._image = image;
        this._options = options;
        this._layers = {};
        this._painted = null;
    }

    putMask(mask, maskLabel = 'default', options = {}) {
        let opt = extendObject({}, this._options, options);
        this._layers[maskLabel] = new ROILayer(mask, opt);
    }

    getROIMap(maskLabel = 'default') {
        if (!this._layers[maskLabel]) return;
        return this._layers[maskLabel].roiMap;
    }

    getROIIDs(maskLabel = 'default', options) {
        let rois = this.getROI(maskLabel, options);
        if (!rois) return;
        let ids = new Array(rois.length);
        for (let i = 0; i < rois.length; i++) {
            ids[i] = rois[i].id;
        }
        return ids;
    }

    getROI(maskLabel = 'default', {
        positive = true,
        negative = true,
        minSurface = 0,
        maxSurface = Number.POSITIVE_INFINITY
        } = {}) {
        let allROIs = this._layers[maskLabel].roi;
        let rois = new Array(allROIs.length);
        let ptr = 0;
        for (let i = 0; i < allROIs.length; i++) {
            let roi = allROIs[i];
            if (((roi.id < 0 && negative) || roi.id > 0 && positive)
                && roi.surface > minSurface
                && roi.surface < maxSurface) {
                rois[ptr++] = roi;
            }
        }
        rois.length = ptr;
        return rois;
    }

    getROIMasks(maskLabel = 'default', options = {}) {
        let rois = this.getROI(maskLabel, options);
        let masks = new Array(rois.length);
        for (let i = 0; i < rois.length; i++) {
            masks[i] = rois[i].mask;
        }
        return masks;
    }

    getPixels(maskLabel = 'default', options = {}) {
        if (this._layers[maskLabel]) {
            return this._layers[maskLabel].roiMap.pixels;
        }
    }

    paint(maskLabel = 'default', options = {}) {
        if (!this._painted) this._painted = this._image.clone();
        let masks = this.getROIMasks(maskLabel, options);
        this._painted.paintMasks(masks, options);
        return this._painted;
    }

    resetPainted() {
        this._painted = undefined;
    }

}

class ROILayer {
    constructor(mask, options) {
        this.mask = mask;
        this.options = options;
        this.roiMap = createROIMap(this.mask, options);
        this.roi = createROI(this.roiMap);
    }
}
<|endoftext|>


<commit_before>import $ from 'jquery';
import { clearStyles } from '../util';

export default function () {
    let jews = {};
    jews.title = $('.article_head .headline').text();
    jews.subtitle = (function () {
        var el = document.querySelector('#articletitle .title h4');
        if (el === null) return undefined;
        return el.innerHTML;
    })();
    jews.content = (function () {
        var content = $('#article_body')[0].cloneNode(true);
        $('[style="display:none;"]', content).remove();
        $('#__inline_ms_da_ad__', content).remove();
        $('#relation_news', content).remove();
        $('span', content).remove();
        return clearStyles(content).innerHTML;
    })();
    jews.timestamp = (function () {
        var text = $('.article_head .byline').text();

        var match = text.match(/입력 (\d{4}).(\d{2}).(\d{2}) (\d{2}):(\d{2})/);
        var created_date = new Date();
        created_date.setYear(parseInt(match[1], 10));
        created_date.setMonth(parseInt(match[2], 10) - 1);
        created_date.setDate(parseInt(match[3], 10));
        created_date.setHours(parseInt(match[4], 10));
        created_date.setMinutes(parseInt(match[5], 10));
        created_date.setSeconds(0);

        var lastModified_date;
        match = text.match(/수정 (\d{4}).(\d{2}).(\d{2}) (\d{2}):(\d{2})/);
        if (match && match.length > 1) {
            lastModified_date = new Date();
            lastModified_date.setYear(parseInt(match[1], 10));
            lastModified_date.setMonth(parseInt(match[2], 10) - 1);
            lastModified_date.setDate(parseInt(match[3], 10));
            lastModified_date.setHours(parseInt(match[4], 10));
            lastModified_date.setMinutes(parseInt(match[5], 10));
            lastModified_date.setSeconds(0);
        }
        return {
            created: created_date,
            lastModified: lastModified_date
        };
    })();
    jews.reporters = (function() {
        var reporters = $('.layer_journalist_wrap');
        var list = [];

        reporters.forEach(function (el) {
            var name = $('.layer_journalist_area .mg a', el).text().trim().match(/(.*?) 기자/)[1];
            var mail, mail_el = $('.share_area a', el);
            if (mail_el.attr('href') !== null) mail = mail_el.attr('href').replace(/mailto:/, '');

            list.push({
                name: name,
                mail: mail
            });
        });

        return list;
    })();
    jews.cleanup = function () {
        $('body>iframe, #gnb_banner, #criteo_network').remove();
    };
    return jews;
}
<commit_msg>중앙일보 기자 이름 파싱 에러 수정<commit_after>import $ from 'jquery';
import { clearStyles } from '../util';

export default function () {
    let jews = {};
    jews.title = $('.article_head .headline').text();
    jews.subtitle = (function () {
        var el = document.querySelector('#articletitle .title h4');
        if (el === null) return undefined;
        return el.innerHTML;
    })();
    jews.content = (function () {
        var content = $('#article_body')[0].cloneNode(true);
        $('[style="display:none;"]', content).remove();
        $('#__inline_ms_da_ad__', content).remove();
        $('#relation_news', content).remove();
        $('span', content).remove();
        return clearStyles(content).innerHTML;
    })();
    jews.timestamp = (function () {
        var text = $('.article_head .byline').text();

        var match = text.match(/입력 (\d{4}).(\d{2}).(\d{2}) (\d{2}):(\d{2})/);
        var created_date = new Date();
        created_date.setYear(parseInt(match[1], 10));
        created_date.setMonth(parseInt(match[2], 10) - 1);
        created_date.setDate(parseInt(match[3], 10));
        created_date.setHours(parseInt(match[4], 10));
        created_date.setMinutes(parseInt(match[5], 10));
        created_date.setSeconds(0);

        var lastModified_date;
        match = text.match(/수정 (\d{4}).(\d{2}).(\d{2}) (\d{2}):(\d{2})/);
        if (match && match.length > 1) {
            lastModified_date = new Date();
            lastModified_date.setYear(parseInt(match[1], 10));
            lastModified_date.setMonth(parseInt(match[2], 10) - 1);
            lastModified_date.setDate(parseInt(match[3], 10));
            lastModified_date.setHours(parseInt(match[4], 10));
            lastModified_date.setMinutes(parseInt(match[5], 10));
            lastModified_date.setSeconds(0);
        }
        return {
            created: created_date,
            lastModified: lastModified_date
        };
    })();
    jews.reporters = (function() {
        var reporters = $('.layer_journalist_wrap');
        var list = [];

        reporters.each(function (el) {
            var name = $('.layer_journalist_area .mg a', el).text().trim().match(/(.*?) 기자/)[1];
            var mail, mail_el = $('.share_area a', el);
            if (mail_el.attr('href') !== null) mail = mail_el.attr('href').replace(/mailto:/, '');

            list.push({
                name: name,
                mail: mail
            });
        });

        return list;
    })();
    jews.cleanup = function () {
        $('body>iframe, #gnb_banner, #criteo_network').remove();
    };
    return jews;
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

require('dotenv').config();

/** * Loggers ***/
/* Main logger */
var winston = require('winston');

/* Allow rotate log files by date */
require('winston-daily-rotate-file');

/* Express middleware logger */
var morgan = require('morgan');
/** */

/* File system */
var fs = require('fs');

/** Setup loggers **/

var logsDir = './logs',
  accessDir = logsDir + '/access',
  errorsDir = logsDir + '/errors';

if (!fs.existsSync(logsDir)) {

  fs.mkdirSync(logsDir);
  fs.mkdirSync(accessDir);
  fs.mkdirSync(errorsDir);

}
if (!fs.existsSync(accessDir)) {

  fs.mkdirSync(accessDir);

}
if (!fs.existsSync(errorsDir)) {

  fs.mkdirSync(errorsDir);

}

/* HTTP requests logger */
winston.loggers.add('access', {
  dailyRotateFile: {
    level: 'debug',
    filename: 'logs/access/.log',
    datePattern: 'yyyy-MM',
    prepend: true,
    timestamp: true
  }
});

/* Global logger */
winston.loggers.add('console', {
  console: {
    level: 'debug',
    colorize: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true
  },
  dailyRotateFile: {
    level: 'debug',
    filename: 'logs/errors/.log',
    datePattern: 'yyyy-MM',
    prepend: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true
  }
});

/**
  * @usage logger.log(level, message)
  *        logger[level](message)
  */
global.logger = winston.loggers.get('console');

let accessLogger = winston.loggers.get('access');

accessLogger.stream = {

  write: function (message) {

    accessLogger.info(message);

  }

};

var app = express();

/**
 * User model
 * @uses  for getting current user data
 */
let user = require('./models/user');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'twig');

// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

/** We use morgan as express middleware to link winston and express **/
app.use(morgan('combined', { stream: accessLogger.stream }));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use('/static', express.static(path.join(__dirname, 'public')));

/**
* Sets response scoped variables
*
* res.locals.user        — current authored user
* res.locals.userDomains — domains list for current user
*
* @fires user.getInfo
*/
app.use(function (req, res, next) {

  user.getInfo(req).then(function (userData) {

    res.locals.user = userData.user;
    res.locals.userDomains = userData.domains;
    next();

  });

});

/**
 * Garage
 */
var garage = require('./routes/garage/garage');

app.use('/garage', garage);

/**
 * Yard
 */
var index = require('./routes/yard/index');
var login = require('./routes/yard/auth/login');
var logout = require('./routes/yard/auth/logout');
var join = require('./routes/yard/auth/join');
var websites = require('./routes/yard/websites');

app.use('/', index);
app.use('/websites', websites);
app.use('/login', login);
app.use('/logout', logout);
app.use('/join', join);

/**
 * Catcher
 */
var catcher = require('./routes/catcher/catcher');

app.use('/catcher', catcher);

// catch 404 and forward to error handler
app.use(function (req, res, next) {

  var err = new Error('Not Found');

  err.status = 404;
  next(err);

});

// error handler
app.use(function (err, req, res, next) {

  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = process.env.ENVIRONMENT === 'DEVELOPMENT' ? err : {};

  logger.error('Error thrown: ', err);

  // render the error page
  res.status(err.status || 500);

  let errorPageData;

  if (err.status === 404) {

    errorPageData = {
      title: '404',
      message : 'Page not found'
    };

  } else {

    errorPageData = {
      title: ':(',
      message : 'Sorry, dude. Looks like some of our services is busy.'
    };

  }

  res.render('yard/errors/error', errorPageData);

});

module.exports = app;
<commit_msg>Set default values for user and userDomains<commit_after>'use strict';

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

require('dotenv').config();

/** * Loggers ***/
/* Main logger */
var winston = require('winston');

/* Allow rotate log files by date */
require('winston-daily-rotate-file');

/* Express middleware logger */
var morgan = require('morgan');
/** */

/* File system */
var fs = require('fs');

/** Setup loggers **/

var logsDir = './logs',
  accessDir = logsDir + '/access',
  errorsDir = logsDir + '/errors';

if (!fs.existsSync(logsDir)) {

  fs.mkdirSync(logsDir);
  fs.mkdirSync(accessDir);
  fs.mkdirSync(errorsDir);

}
if (!fs.existsSync(accessDir)) {

  fs.mkdirSync(accessDir);

}
if (!fs.existsSync(errorsDir)) {

  fs.mkdirSync(errorsDir);

}

/* HTTP requests logger */
winston.loggers.add('access', {
  dailyRotateFile: {
    level: 'debug',
    filename: 'logs/access/.log',
    datePattern: 'yyyy-MM',
    prepend: true,
    timestamp: true
  }
});

/* Global logger */
winston.loggers.add('console', {
  console: {
    level: 'debug',
    colorize: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true
  },
  dailyRotateFile: {
    level: 'debug',
    filename: 'logs/errors/.log',
    datePattern: 'yyyy-MM',
    prepend: true,
    timestamp: true,
    handleExceptions: true,
    humanReadableUnhandledException: true
  }
});

/**
  * @usage logger.log(level, message)
  *        logger[level](message)
  */
global.logger = winston.loggers.get('console');

let accessLogger = winston.loggers.get('access');

accessLogger.stream = {

  write: function (message) {

    accessLogger.info(message);

  }

};

var app = express();

/**
 * User model
 * @uses  for getting current user data
 */
let user = require('./models/user');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'twig');

// uncomment after placing your favicon in /public
// app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));

/** We use morgan as express middleware to link winston and express **/
app.use(morgan('combined', { stream: accessLogger.stream }));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use('/static', express.static(path.join(__dirname, 'public')));

/**
* Sets response scoped variables
*
* res.locals.user        — current authored user
* res.locals.userDomains — domains list for current user
*
* @fires user.getInfo
*/
app.use(function (req, res, next) {

  res.locals.user = {};
  res.locals.userDomains = {};

  user.getInfo(req).then(function (userData) {

    res.locals.user = userData.user;
    res.locals.userDomains = userData.domains;
    next();

  }).catch(function (e) {

    logger.error(e);
    next();

  });

});

/**
 * Garage
 */
var garage = require('./routes/garage/garage');

app.use('/garage', garage);

/**
 * Yard
 */
var index = require('./routes/yard/index');
var login = require('./routes/yard/auth/login');
var logout = require('./routes/yard/auth/logout');
var join = require('./routes/yard/auth/join');
var websites = require('./routes/yard/websites');

app.use('/', index);
app.use('/websites', websites);
app.use('/login', login);
app.use('/logout', logout);
app.use('/join', join);

/**
 * Catcher
 */
var catcher = require('./routes/catcher/catcher');

app.use('/catcher', catcher);

// catch 404 and forward to error handler
app.use(function (req, res, next) {

  var err = new Error('Not Found');

  err.status = 404;
  next(err);

});

// error handler
app.use(function (err, req, res, next) {

  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = process.env.ENVIRONMENT === 'DEVELOPMENT' ? err : {};

  logger.error('Error thrown: ', err);

  // render the error page
  res.status(err.status || 500);

  let errorPageData;

  if (err.status === 404) {

    errorPageData = {
      title: '404',
      message : 'Page not found'
    };

  } else {

    errorPageData = {
      title: ':(',
      message : 'Sorry, dude. Looks like some of our services is busy.'
    };

  }

  res.render('yard/errors/error', errorPageData);

});

module.exports = app;
<|endoftext|>


<commit_before>#!/usr/bin/env node
/*jshint node:true, es5:true */
var argv = require('optimist').
        usage('Usage: $0 [options] <doc.md ...>').
        demand(1).
        boolean('n').
        describe('n', 'Turn off numbered sections').
        boolean('h').
        describe('h', 'Turn on bootstrap page header.').
        describe('outputdir', 'Directory to put the converted files in.').
        default('outputdir', '.').
        argv,
    pagedown = require('pagedown'),
    converter = new pagedown.Converter(),
    path = require('path'),
    fs = require('fs'),
    top_part = fs.readFileSync(__dirname + "/parts/top.html").toString(),
    bottom_part = fs.readFileSync(__dirname + "/parts/bottom.html").toString(),
    levels, toc, nextId;

function findTag(md, tag, obj) {
    var re = new RegExp("^", "m"), match = md.match(re);

    if (!obj) { return; }

    if (match) {
        obj[tag] = match[1];
    }
}

// Configure section and toc generation
converter.hooks.set("postConversion", function(text) {
    return text.replace(/<(h(\d))>/g, function(match, p1, p2, offset, str) {
        var i, levelStr = "";

        levels[p1] = levels[p1] || 0;

        // Figure out section number
	if (!argv.n) {
            // reset lower levels
            for (i = Number(p2) + 1; levels["h"+i]; i++) {
                levels["h"+i] = 0;
            }

            // grab higher levels
            for (i = Number(p2) - 1; levels["h"+i]; i--) {
                levelStr = levels["h"+i] + "." + levelStr;
            }

            levels[p1] = levels[p1] + 1;
            levelStr = levelStr + levels[p1] + ". ";
        }

        // Add toc entry
        toc.push({
            levelStr: levelStr,
            id: ++nextId,
            title: str.slice(offset+4, str.slice(offset).indexOf("</h")+offset)
        });

        return "<h" + p2 + ' id="' + nextId + '">' + levelStr;
    }).replace(/
/g, '
');
});

// Create output directory
if (!fs.existsSync('html')) {
    fs.mkdirSync('html');
}

argv._.forEach(function(md_path) {
    var tags = { title: "TITLE HERE", subtitle: "SUBTITLE HERE" },
        md, output, tocHtml = "",
        output_path = path.join(argv.outputdir, path.basename(md_path));

    // Determine output filename
    if (/\.md$/.test(output_path)) {
        output_path = output_path.slice(0, -3);
    }
    output_path += '.html';

    // Read markdown in
    md = fs.readFileSync(md_path).toString();

    // Find title and subtitle tags in document
    findTag(md, "title", tags);
    findTag(md, "subtitle", tags);

    levels = {}; nextId = 0; toc = [];
    output = converter.makeHtml(md);

    // Add table of contents
    tocHtml += '

';
    toc.forEach(function(entry) {
        tocHtml += '
' + entry.levelStr + entry.title + '
';
    });
    tocHtml += '

';

    // Bootstrap-fy
    output =
        top_part.replace(/\{\{header\}\}/, function() {
            if (argv.h) {
                return '' +
                       '
' +
                       '
' + tags.title  + '
' +
                       '
' + tags.subtitle + '
' +
                       '
';
            } else {
                return "";
            }
        }).replace(/\{\{title\}\}/, tags.title === "TITLE HERE" ? "" : tags.title) +
        tocHtml +
        output +
        bottom_part;

    fs.writeFileSync(output_path, output);
    console.log("Converted " + md_path + " to " + output_path);
});
<commit_msg>Fix outputdir resolution<commit_after>#!/usr/bin/env node
/*jshint node:true, es5:true */
var argv = require('optimist').
        usage('Usage: $0 [options] <doc.md ...>').
        demand(1).
        boolean('n').
        describe('n', 'Turn off numbered sections').
        boolean('h').
        describe('h', 'Turn on bootstrap page header.').
        describe('outputdir', 'Directory to put the converted files in.').
        default('outputdir', '.').
        argv,
    pagedown = require('pagedown'),
    converter = new pagedown.Converter(),
    path = require('path'),
    fs = require('fs'),
    top_part = fs.readFileSync(__dirname + "/parts/top.html").toString(),
    bottom_part = fs.readFileSync(__dirname + "/parts/bottom.html").toString(),
    levels, toc, nextId;

function findTag(md, tag, obj) {
    var re = new RegExp("^", "m"), match = md.match(re);

    if (!obj) { return; }

    if (match) {
        obj[tag] = match[1];
    }
}

// Configure section and toc generation
converter.hooks.set("postConversion", function(text) {
    return text.replace(/<(h(\d))>/g, function(match, p1, p2, offset, str) {
        var i, levelStr = "";

        levels[p1] = levels[p1] || 0;

        // Figure out section number
	if (!argv.n) {
            // reset lower levels
            for (i = Number(p2) + 1; levels["h"+i]; i++) {
                levels["h"+i] = 0;
            }

            // grab higher levels
            for (i = Number(p2) - 1; levels["h"+i]; i--) {
                levelStr = levels["h"+i] + "." + levelStr;
            }

            levels[p1] = levels[p1] + 1;
            levelStr = levelStr + levels[p1] + ". ";
        }

        // Add toc entry
        toc.push({
            levelStr: levelStr,
            id: ++nextId,
            title: str.slice(offset+4, str.slice(offset).indexOf("</h")+offset)
        });

        return "<h" + p2 + ' id="' + nextId + '">' + levelStr;
    }).replace(/
/g, '
');
});

// Create output directory
argv.outputdir = path.resolve(process.cwd(), argv.outputdir);
if (!fs.existsSync(argv.outputdir)) {
    fs.mkdirSync(argv.outputdir);
}

argv._.forEach(function(md_path) {
    var tags = { title: "TITLE HERE", subtitle: "SUBTITLE HERE" },
        md, output, tocHtml = "",
        output_path = path.join(argv.outputdir, path.basename(md_path));

    // Determine output filename
    if (/\.md$/.test(output_path)) {
        output_path = output_path.slice(0, -3);
    }
    output_path += '.html';

    // Read markdown in
    md = fs.readFileSync(md_path).toString();

    // Find title and subtitle tags in document
    findTag(md, "title", tags);
    findTag(md, "subtitle", tags);

    levels = {}; nextId = 0; toc = [];
    output = converter.makeHtml(md);

    // Add table of contents
    tocHtml += '

';
    toc.forEach(function(entry) {
        tocHtml += '
' + entry.levelStr + entry.title + '
';
    });
    tocHtml += '

';

    // Bootstrap-fy
    output =
        top_part.replace(/\{\{header\}\}/, function() {
            if (argv.h) {
                return '' +
                       '
' +
                       '
' + tags.title  + '
' +
                       '
' + tags.subtitle + '
' +
                       '
';
            } else {
                return "";
            }
        }).replace(/\{\{title\}\}/, tags.title === "TITLE HERE" ? "" : tags.title) +
        tocHtml +
        output +
        bottom_part;

    fs.writeFileSync(output_path, output);
    console.log("Converted " + md_path + " to " + path.relative(process.cwd(), output_path));
});
<|endoftext|>


<commit_before>import { registerRootComponent } from 'expo';
import { activateKeepAwake } from 'expo-keep-awake';

import App from '../../App';

if (__DEV__) {
  activateKeepAwake();
}

registerRootComponent(App);
<commit_msg>Use registerRootComponent directly (#6008)<commit_after>import registerRootComponent from 'expo/build/launch/registerRootComponent';
import { activateKeepAwake } from 'expo-keep-awake';

import App from '../../App';

if (__DEV__) {
  activateKeepAwake();
}

registerRootComponent(App);
<|endoftext|>


<commit_before>exports.yargs = {
  command: 'diff <fileA> <fileB>',
  describe: 'Perform a diff between two recon files',
  aliases: ['d'],

  builder: (yargs) => {
    yargs.option('subset', {
      alias: 's',
      choices: ['left', 'right', 'both'],
      default: 'left',
      describe: 'The subset to select',
    })

    const { installWriteOptions } = require('../../lib/handlers/file')

    installWriteOptions(yargs)

    const { installOutputOptions } = require('../../lib/handlers/output')

    installOutputOptions(yargs)
  },

  handler: async (argv) => {
    const { fileA, fileB, subset } = argv

    const fs = require('fs')

    const { Recon } = require('../../../../lib/recon')

    let fileAData

    try {
      fileAData = await fs.promises.readFile(fileA)
    } catch (e) {
      console.error(`Cannot read file ${fileA}`)

      return
    }

    let fileAJSON

    try {
      fileAJSON = JSON.parse(fileAData, (key, value) => {
        if (value && value.type === 'Buffer' && Array.isArray(value.data)) {
          return Buffer.from(value.data)
        } else {
          return value
        }
      })
    } catch (e) {
      console.error(`Cannot unpack file ${fileA}`)

      return
    }

    const reconA = new Recon()

    reconA.on('info', console.info.bind(console))
    reconA.on('warn', console.warn.bind(console))
    reconA.on('error', console.error.bind(console))
    reconA.on('debug', console.debug.bind(console))

    try {
      reconA.deserialize(fileAJSON)
    } catch (e) {
      console.error(`Cannot load file ${fileA}`)

      return
    }

    let fileBData

    try {
      fileBData = await readFile(fileB)
    } catch (e) {
      console.error(`Cannot read file ${fileB}`)

      return
    }

    let fileBJSON

    try {
      fileBJSON = JSON.parse(fileBData, (key, value) => {
        if (value && value.type === 'Buffer' && Array.isArray(value.data)) {
          return Buffer.from(value.data)
        } else {
          return value
        }
      })
    } catch (e) {
      console.error(`Cannot parse file ${fileB}`)

      return
    }

    const reconB = new Recon()

    reconB.on('info', console.info.bind(console))
    reconB.on('warn', console.warn.bind(console))
    reconB.on('error', console.error.bind(console))
    reconB.on('debug', console.debug.bind(console))

    try {
      reconB.deserialize(fileBJSON)
    } catch (e) {
      console.error(`Cannot load file ${fileB}`)

      return
    }

    const { [subset]: collection } = reconA.cy
      .elements()
      .diff(reconB.cy.elements())

    const resultNodes = collection.map((node) => node.data())

    const { handleWriteOptions } = require('../../lib/handlers/file')

    const recon = new Recon()

    recon.on('info', console.info.bind(console))
    recon.on('warn', console.warn.bind(console))
    recon.on('error', console.error.bind(console))
    recon.on('debug', console.debug.bind(console))

    recon.cy.add({
      group: 'nodes',
      data: {
        id: 'previous',
        type: 'previous',
        label: 'Previous',
        props: {},
      },
    })

    collection.nodes().forEach((node) => {
      recon.cy.add(node)
    })

    collection.edges().forEach((edge) => {
      const data = edge.data()

      let { source, target } = data

      let move

      if (!source || !collection.getElementById(source).length) {
        source = 'previous'
        move = true
      }

      if (!target || !collection.getElementById(target).length) {
        target = 'previous'
        move = true
      }

      if (move) {
        recon.cy.add({
          group: 'edges',
          data: { ...data, source, target },
        })
      } else {
        recon.cy.add({
          group: 'edges',
          data: data,
        })
      }
    })

    await handleWriteOptions(argv, recon)

    const { handleOutputOptions } = require('../../lib/handlers/output')

    await handleOutputOptions(argv, resultNodes)
  },
}
<commit_msg>Fixed a bug.<commit_after>exports.yargs = {
  command: 'diff <fileA> <fileB>',
  describe: 'Perform a diff between two recon files',
  aliases: ['d'],

  builder: (yargs) => {
    yargs.option('subset', {
      alias: 's',
      choices: ['left', 'right', 'both'],
      default: 'left',
      describe: 'The subset to select',
    })

    const { installWriteOptions } = require('../../lib/handlers/file')

    installWriteOptions(yargs)

    const { installOutputOptions } = require('../../lib/handlers/output')

    installOutputOptions(yargs)
  },

  handler: async (argv) => {
    const { fileA, fileB, subset } = argv

    const fs = require('fs')

    const { Recon } = require('../../../../lib/recon')

    let fileAData

    try {
      fileAData = await fs.promises.readFile(fileA)
    } catch (e) {
      console.error(`Cannot read file ${fileA}`)

      return
    }

    let fileAJSON

    try {
      fileAJSON = JSON.parse(fileAData, (key, value) => {
        if (value && value.type === 'Buffer' && Array.isArray(value.data)) {
          return Buffer.from(value.data)
        } else {
          return value
        }
      })
    } catch (e) {
      console.error(`Cannot unpack file ${fileA}`)

      return
    }

    const reconA = new Recon()

    reconA.on('info', console.info.bind(console))
    reconA.on('warn', console.warn.bind(console))
    reconA.on('error', console.error.bind(console))
    reconA.on('debug', console.debug.bind(console))

    try {
      reconA.deserialize(fileAJSON)
    } catch (e) {
      console.error(`Cannot load file ${fileA}`)

      return
    }

    let fileBData

    try {
      fileBData = await fs.promises.readFile(fileB)
    } catch (e) {
      console.error(`Cannot read file ${fileB}`)

      return
    }

    let fileBJSON

    try {
      fileBJSON = JSON.parse(fileBData, (key, value) => {
        if (value && value.type === 'Buffer' && Array.isArray(value.data)) {
          return Buffer.from(value.data)
        } else {
          return value
        }
      })
    } catch (e) {
      console.error(`Cannot parse file ${fileB}`)

      return
    }

    const reconB = new Recon()

    reconB.on('info', console.info.bind(console))
    reconB.on('warn', console.warn.bind(console))
    reconB.on('error', console.error.bind(console))
    reconB.on('debug', console.debug.bind(console))

    try {
      reconB.deserialize(fileBJSON)
    } catch (e) {
      console.error(`Cannot load file ${fileB}`)

      return
    }

    const { [subset]: collection } = reconA.cy
      .elements()
      .diff(reconB.cy.elements())

    const resultNodes = collection.map((node) => node.data())

    const { handleWriteOptions } = require('../../lib/handlers/file')

    const recon = new Recon()

    recon.on('info', console.info.bind(console))
    recon.on('warn', console.warn.bind(console))
    recon.on('error', console.error.bind(console))
    recon.on('debug', console.debug.bind(console))

    recon.cy.add({
      group: 'nodes',
      data: {
        id: 'previous',
        type: 'previous',
        label: 'Previous',
        props: {},
      },
    })

    collection.nodes().forEach((node) => {
      recon.cy.add(node)
    })

    collection.edges().forEach((edge) => {
      const data = edge.data()

      let { source, target } = data

      let move

      if (!source || !collection.getElementById(source).length) {
        source = 'previous'
        move = true
      }

      if (!target || !collection.getElementById(target).length) {
        target = 'previous'
        move = true
      }

      if (move) {
        recon.cy.add({
          group: 'edges',
          data: { ...data, source, target },
        })
      } else {
        recon.cy.add({
          group: 'edges',
          data: data,
        })
      }
    })

    await handleWriteOptions(argv, recon)

    const { handleOutputOptions } = require('../../lib/handlers/output')

    await handleOutputOptions(argv, resultNodes)
  },
}
<|endoftext|>


<commit_before>import glob from 'glob'
import { exclude } from './config.json'

export default async (dir, ignore = []) => {
  return new Promise(resolve => {
    glob('**',
      {ignore: [...ignore, ...exclude], cwd: dir, follow: false},
      (err, files) => {
        files = err ? null : files
        resolve(files)
      }
    )
  })
}
<commit_msg>[pkg] ls: use wildcards<commit_after>import glob from 'glob'
import { exclude } from './config.json'

export default async (dir, ignore = []) => {
  return new Promise(resolve => {
    glob('*',
      {ignore: [...ignore, ...exclude], cwd: dir, follow: false},
      (err, files) => {
        files = err ? null : files
        resolve(files)
      }
    )
  })
}
<|endoftext|>


<commit_before>// XXX We should revisit how we factor MongoDB support into (1) the
// server-side node.js driver [which you might use independently of
// livedata, after all], (2) minimongo [ditto], and (3) Collection,
// which is the class that glues the two of them to Livedata, but also
// is generally the "public interface for newbies" to Mongo in the
// Meteor universe. We want to allow the components to be used
// independently, but we don't want to overwhelm the user with
// minutiae.

Package.describe({
  summary: "Adaptor for using MongoDB and Minimongo over DDP",
  version: '1.0.3'
});

Npm.depends({
  mongodb: "https://github.com/meteor/node-mongodb-native/tarball/cbd6220ee17c3178d20672b4a1df80f82f97d4c1"
});

Package.on_use(function (api) {
  api.use(['random', 'ejson', 'json', 'underscore', 'minimongo', 'logging',
           'ddp', 'tracker', 'application-configuration'],
          ['client', 'server']);
  api.use('check', ['client', 'server']);

  // Binary Heap data structure is used to optimize oplog observe driver
  // performance.
  api.use('binary-heap', 'server');

  // Allow us to detect 'insecure'.
  api.use('insecure', {weak: true});

  // Allow us to detect 'autopublish', and publish collections if it's loaded.
  api.use('autopublish', 'server', {weak: true});

  // Allow us to detect 'disable-oplog', which turns off oplog tailing for your
  // app even if it's configured in the environment. (This package will be
  // probably be removed before 1.0.)
  api.use('disable-oplog', 'server', {weak: true});

  // defaultRemoteCollectionDriver gets its deployConfig from something that is
  // (for questionable reasons) initialized by the webapp package.
  api.use('webapp', 'server', {weak: true});

  // If the facts package is loaded, publish some statistics.
  api.use('facts', 'server', {weak: true});

  api.use('callback-hook', 'server');

  // Stuff that should be exposed via a real API, but we haven't yet.
  api.export('MongoInternals', 'server');
  // For tests only.
  api.export('MongoTest', 'server', {testOnly: true});

  api.add_files(['mongo_driver.js', 'oplog_tailing.js',
                 'observe_multiplex.js', 'doc_fetcher.js',
                 'polling_observe_driver.js','oplog_observe_driver.js'],
                'server');
  api.add_files('local_collection_driver.js', ['client', 'server']);
  api.add_files('remote_collection_driver.js', 'server');
  api.add_files('collection.js', ['client', 'server']);
});

Package.on_test(function (api) {
  api.use('mongo');
  api.use('check');
  api.use(['tinytest', 'underscore', 'test-helpers', 'ejson', 'random',
           'ddp']);
  // XXX test order dependency: the allow_tests "partial allow" test
  // fails if it is run before mongo_livedata_tests.
  api.add_files('mongo_livedata_tests.js', ['client', 'server']);
  api.add_files('allow_tests.js', ['client', 'server']);
  api.add_files('collection_tests.js', ['client', 'server']);
  api.add_files('observe_changes_tests.js', ['client', 'server']);
  api.add_files('oplog_tests.js', 'server');
  api.add_files('doc_fetcher_tests.js', 'server');
});
<commit_msg>Add Mongo export<commit_after>// XXX We should revisit how we factor MongoDB support into (1) the
// server-side node.js driver [which you might use independently of
// livedata, after all], (2) minimongo [ditto], and (3) Collection,
// which is the class that glues the two of them to Livedata, but also
// is generally the "public interface for newbies" to Mongo in the
// Meteor universe. We want to allow the components to be used
// independently, but we don't want to overwhelm the user with
// minutiae.

Package.describe({
  summary: "Adaptor for using MongoDB and Minimongo over DDP",
  version: '1.0.3'
});

Npm.depends({
  mongodb: "https://github.com/meteor/node-mongodb-native/tarball/cbd6220ee17c3178d20672b4a1df80f82f97d4c1"
});

Package.on_use(function (api) {
  api.use(['random', 'ejson', 'json', 'underscore', 'minimongo', 'logging',
           'ddp', 'tracker', 'application-configuration'],
          ['client', 'server']);
  api.use('check', ['client', 'server']);

  // Binary Heap data structure is used to optimize oplog observe driver
  // performance.
  api.use('binary-heap', 'server');

  // Allow us to detect 'insecure'.
  api.use('insecure', {weak: true});

  // Allow us to detect 'autopublish', and publish collections if it's loaded.
  api.use('autopublish', 'server', {weak: true});

  // Allow us to detect 'disable-oplog', which turns off oplog tailing for your
  // app even if it's configured in the environment. (This package will be
  // probably be removed before 1.0.)
  api.use('disable-oplog', 'server', {weak: true});

  // defaultRemoteCollectionDriver gets its deployConfig from something that is
  // (for questionable reasons) initialized by the webapp package.
  api.use('webapp', 'server', {weak: true});

  // If the facts package is loaded, publish some statistics.
  api.use('facts', 'server', {weak: true});

  api.use('callback-hook', 'server');

  // Stuff that should be exposed via a real API, but we haven't yet.
  api.export('MongoInternals', 'server');
  // For tests only.
  api.export('MongoTest', 'server', {testOnly: true});
  api.export("Mongo");

  api.add_files(['mongo_driver.js', 'oplog_tailing.js',
                 'observe_multiplex.js', 'doc_fetcher.js',
                 'polling_observe_driver.js','oplog_observe_driver.js'],
                'server');
  api.add_files('local_collection_driver.js', ['client', 'server']);
  api.add_files('remote_collection_driver.js', 'server');
  api.add_files('collection.js', ['client', 'server']);
});

Package.on_test(function (api) {
  api.use('mongo');
  api.use('check');
  api.use(['tinytest', 'underscore', 'test-helpers', 'ejson', 'random',
           'ddp']);
  // XXX test order dependency: the allow_tests "partial allow" test
  // fails if it is run before mongo_livedata_tests.
  api.add_files('mongo_livedata_tests.js', ['client', 'server']);
  api.add_files('allow_tests.js', ['client', 'server']);
  api.add_files('collection_tests.js', ['client', 'server']);
  api.add_files('observe_changes_tests.js', ['client', 'server']);
  api.add_files('oplog_tests.js', 'server');
  api.add_files('doc_fetcher_tests.js', 'server');
});
<|endoftext|>


<commit_before>/**
 * PostCSS webpack block.
 *
 * @see https://github.com/postcss/postcss-loader
 */

module.exports = postcss

/**
 * @param {PostCSSPlugin[]} [plugins]                   Will read `postcss.config.js` file if not supplied.
 * @param {object}          [options]
 * @param {RegExp|Function|string}  [options.exclude]     Directories to exclude.
 * @param {string}                  [options.parser]      Package name of custom PostCSS parser to use.
 * @param {string}                  [options.stringifier] Package name of custom PostCSS stringifier to use.
 * @param {string}                  [options.syntax]      Package name of custom PostCSS parser/stringifier to use.
 * @return {Function}
 */
function postcss (plugins, options) {
  plugins = plugins || []
  options = options || {}

  // https://github.com/postcss/postcss-loader#options
  const postcssOptions = Object.assign(
    {},
    options.parser && { parser: options.parser },
    options.stringifier && { stringifier: options.stringifier },
    options.syntax && { syntax: options.syntax }
  )

  return (context) => Object.assign(
    {
      module: {
        loaders: [
          Object.assign({
            test: context.fileType('text/css'),
            loaders: [ 'style-loader', 'css-loader', 'postcss-loader?' + JSON.stringify(postcssOptions) ]
          }, options.exclude ? {
            exclude: Array.isArray(options.exclude) ? options.exclude : [ options.exclude ]
          } : {})
        ]
      }
    },
    plugins ? createPostcssPluginsConfig(context.webpack, plugins) : {}
  )
}

function createPostcssPluginsConfig (webpack, plugins) {
  const isWebpack2 = typeof webpack.validateSchema !== 'undefined'

  if (isWebpack2) {
    return {
      plugins: [
        new webpack.LoaderOptionsPlugin({
          options: {
            postcss: plugins,

            // Hacky fix for a strange issue involving the postcss-loader, sass-loader and webpack@2
            // (see https://github.com/andywer/webpack-blocks/issues/116)
            // Might be removed again once the `sass` block uses a newer `sass-loader`
            context: '/'
          }
        })
      ]
    }
  } else {
    return {
      postcss: plugins
    }
  }
}
<commit_msg>Fix postcss config<commit_after>/**
 * PostCSS webpack block.
 *
 * @see https://github.com/postcss/postcss-loader
 */

module.exports = postcss

/**
 * @param {PostCSSPlugin[]} [plugins]                   Will read `postcss.config.js` file if not supplied.
 * @param {object}          [options]
 * @param {RegExp|Function|string}  [options.exclude]     Directories to exclude.
 * @param {string}                  [options.parser]      Package name of custom PostCSS parser to use.
 * @param {string}                  [options.stringifier] Package name of custom PostCSS stringifier to use.
 * @param {string}                  [options.syntax]      Package name of custom PostCSS parser/stringifier to use.
 * @return {Function}
 */
function postcss (plugins, options) {
  plugins = plugins || []
  options = options || {}

  // https://github.com/postcss/postcss-loader#options
  const postcssOptions = Object.assign(
    {},
    options.parser && { parser: options.parser },
    options.stringifier && { stringifier: options.stringifier },
    options.syntax && { syntax: options.syntax }
  )

  return (context) => Object.assign(
    {
      module: {
        loaders: [
          Object.assign({
            test: context.fileType('text/css'),
            loaders: [ 'style-loader', 'css-loader', 'postcss-loader?' + JSON.stringify(postcssOptions) ]
          }, options.exclude ? {
            exclude: Array.isArray(options.exclude) ? options.exclude : [ options.exclude ]
          } : {})
        ]
      }
    },
    plugins.length > 0 ? createPostcssPluginsConfig(context.webpack, plugins) : {}
  )
}

function createPostcssPluginsConfig (webpack, plugins) {
  const isWebpack2 = typeof webpack.validateSchema !== 'undefined'

  if (isWebpack2) {
    return {
      plugins: [
        new webpack.LoaderOptionsPlugin({
          options: {
            postcss: plugins,

            // Hacky fix for a strange issue involving the postcss-loader, sass-loader and webpack@2
            // (see https://github.com/andywer/webpack-blocks/issues/116)
            // Might be removed again once the `sass` block uses a newer `sass-loader`
            context: '/'
          }
        })
      ]
    }
  } else {
    return {
      postcss: plugins
    }
  }
}
<|endoftext|>


<commit_before>// @flow
import * as React from 'react'
import * as Kb from '../../../common-adapters'
import * as Styles from '../../../styles'
import {SiteIcon} from '../shared'
import type {SiteIconSet} from '../../../constants/types/tracker2'

type InputProps = {|
  error: boolean,
  onChangeUsername: string => void,
  onEnterKeyDown: () => void,
  serviceIcon: SiteIconSet,
  serviceSuffix: string,
  username: string,
|}

type InputState = {|
  focus: boolean,
  username: string,
|}

class EnterUsernameInput extends React.Component<InputProps, InputState> {
  state = {focus: false, username: this.props.username}

  _onChangeUsername = username => {
    this.props.onChangeUsername(username)
    this.setState({username})
  }

  _setFocus = focus => this.setState(s => (s.focus === focus ? null : {focus}))
  _onFocus = () => this._setFocus(true)
  _onBlur = () => this._setFocus(false)

  render() {
    return (
      <Kb.Box2
        direction="vertical"
        style={Styles.collapseStyles([
          styles.inputBox,
          this.state.username && styles.inputBoxSmall,
          this.state.focus && styles.borderBlue,
          this.props.error && styles.borderRed,
        ])}
        fullWidth={true}
      >
        {!!this.state.username && (
          <Kb.Text type="BodySmallSemibold" style={styles.colorBlue}>
            Your username
          </Kb.Text>
        )}
        <Kb.Box2 direction="horizontal" gap="xtiny" alignItems="center" fullWidth={true}>
          <SiteIcon
            set={this.props.serviceIcon}
            full={false}
            style={this.state.username ? styles.opacity75 : styles.opacity40}
          />
          <Kb.Box2 direction="horizontal" style={styles.positionRelative} fullWidth={true}>
            <Kb.PlainInput
              autoFocus={true}
              flexable={true}
              textType="BodySemibold"
              value={this.state.username}
              onChangeText={this._onChangeUsername}
              onEnterKeyDown={this.props.onEnterKeyDown}
              onFocus={this._onFocus}
              onBlur={this._onBlur}
              style={styles.input}
            />
            <Kb.Box2 direction="horizontal" style={styles.inputPlaceholder} pointerEvents="none">
              <Kb.Text type="BodySemibold" lineClamp={1} style={styles.paddingRightTiny}>
                <Kb.Text
                  type="BodySemibold"
                  style={Styles.collapseStyles([
                    styles.placeholder,
                    !!this.state.username && styles.invisible,
                  ])}
                >
                  {this.state.username || 'Your username'}
                </Kb.Text>
                <Kb.Text type="BodySemibold" style={styles.placeholderService}>
                  {this.props.serviceSuffix}
                </Kb.Text>
              </Kb.Text>
            </Kb.Box2>
          </Kb.Box2>
        </Kb.Box2>
      </Kb.Box2>
    )
  }
}

const Unreachable = props => (
  <Kb.Box2
    direction="horizontal"
    gap="xtiny"
    alignItems="flex-start"
    style={Styles.collapseStyles([styles.inputBox, styles.unreachableBox])}
    fullWidth={Styles.isMobile}
  >
    <SiteIcon
      set={props.serviceIcon}
      full={false}
      style={Styles.collapseStyles([styles.opacity75, styles.inlineIcon])}
    />
    <Kb.Box2 direction="vertical" style={styles.flexOne}>
      <Kb.Text type="BodySemibold" style={styles.placeholder}>
        <Kb.Text type="BodySemibold" style={styles.colorRed}>
          {props.username}
        </Kb.Text>
        {props.serviceSuffix}
      </Kb.Text>
      <Kb.Meta title="unreachable" backgroundColor={Styles.globalColors.red} />
    </Kb.Box2>
    <Kb.Icon
      type="iconfont-proof-broken"
      color={Styles.globalColors.red}
      boxStyle={styles.marginLeftAuto}
      style={Kb.iconCastPlatformStyles(styles.inlineIcon)}
    />
  </Kb.Box2>
)

type Props = {|
  error: string,
  onBack: () => void,
  onCancel: () => void,
  onChangeUsername: string => void,
  onContinue: () => void,
  onSubmit: () => void,
  serviceIcon: SiteIconSet,
  serviceIconFull: SiteIconSet,
  serviceName: string,
  serviceSub: string,
  serviceSuffix: string,
  submitButtonLabel: string,
  unreachable: boolean,
  username: string,
  waiting: boolean, // waiting goes true -> false when proof is complete
|}

class _EnterUsername extends React.Component<Props> {
  componentDidUpdate(prevProps: Props) {
    if (!this.props.waiting && prevProps.waiting) {
      this.props.onContinue()
    }
  }
  render() {
    const props = this.props
    return (
      <Kb.Box2 direction="vertical" fullWidth={true} fullHeight={true} style={styles.container}>
        {!props.unreachable && !Styles.isMobile && (
          <Kb.BackButton onClick={props.onBack} style={styles.backButton} />
        )}
        <Kb.Box2
          alignItems="center"
          direction="vertical"
          gap="xtiny"
          style={styles.serviceIconHeaderContainer}
        >
          <Kb.Box2 direction="vertical" style={styles.positionRelative}>
            <SiteIcon set={props.serviceIconFull} full={true} style={styles.serviceIconFull} />
            <Kb.Icon
              type={props.unreachable ? 'icon-proof-broken' : 'icon-proof-unfinished'}
              style={styles.serviceProofIcon}
            />
          </Kb.Box2>
          <Kb.Box2 direction="vertical" alignItems="center">
            <Kb.Text type="BodySemibold">{props.serviceName}</Kb.Text>
            <Kb.Text type="BodySmall">{props.serviceSub}</Kb.Text>
          </Kb.Box2>
        </Kb.Box2>
        <Kb.Box2
          fullWidth={true}
          direction="vertical"
          alignItems="flex-start"
          gap="xtiny"
          style={styles.inputContainer}
        >
          {props.unreachable ? (
            <Unreachable
              serviceIcon={props.serviceIcon}
              serviceSuffix={props.serviceSuffix}
              username={props.username}
            />
          ) : (
            <EnterUsernameInput
              error={!!props.error}
              serviceIcon={props.serviceIcon}
              serviceSuffix={props.serviceSuffix}
              username={props.username}
              onChangeUsername={props.onChangeUsername}
              onEnterKeyDown={props.onSubmit}
            />
          )}
          {!!props.error && <Kb.Text type="BodySmallError">{props.error}</Kb.Text>}
        </Kb.Box2>
        <Kb.Box2
          alignItems="center"
          fullWidth={true}
          direction="vertical"
          style={props.unreachable ? styles.buttonBarWarning : null}
        >
          {props.unreachable && (
            <Kb.Text type="BodySmallSemibold" center={true} style={styles.warningText}>
              You need to authorize your proof on {props.serviceName}.
            </Kb.Text>
          )}
          <Kb.ButtonBar direction="row" fullWidth={true} style={styles.buttonBar}>
            {!Styles.isMobile && !props.unreachable && (
              <Kb.Button type="Secondary" onClick={props.onBack} label="Cancel" style={styles.buttonSmall} />
            )}
            {props.unreachable ? (
              <Kb.Button
                type="PrimaryGreen"
                onClick={props.onSubmit}
                label={props.submitButtonLabel}
                style={styles.buttonBig}
              />
            ) : (
              <Kb.WaitingButton
                type="PrimaryGreen"
                onClick={props.onSubmit}
                label={props.submitButtonLabel}
                style={styles.buttonBig}
                waitingKey={null}
              />
            )}
          </Kb.ButtonBar>
        </Kb.Box2>
      </Kb.Box2>
    )
  }
}
const EnterUsername = Kb.HeaderOrPopup(_EnterUsername)

const styles = Styles.styleSheetCreate({
  backButton: {left: Styles.globalMargins.small, position: 'absolute', top: Styles.globalMargins.small},
  borderBlue: {borderColor: Styles.globalColors.blue},
  borderRed: {borderColor: Styles.globalColors.red},
  buttonBar: {
    ...Styles.padding(Styles.globalMargins.small, Styles.globalMargins.medium, Styles.globalMargins.medium),
  },
  buttonBarWarning: {backgroundColor: Styles.globalColors.yellow},
  buttonBig: {flex: 2.5},
  buttonSmall: {flex: 1},
  colorBlue: {color: Styles.globalColors.blue},
  colorRed: {color: Styles.globalColors.red},
  container: Styles.platformStyles({isElectron: {height: 485, width: 560}}),
  flexOne: {flex: 1},
  inlineIcon: {
    position: 'relative',
    top: 1,
  },
  input: Styles.platformStyles({
    common: {marginRight: Styles.globalMargins.medium},
    isElectron: {
      marginTop: -1,
    },
    isMobile: {
      top: 3,
    },
  }),
  inputBox: {
    ...Styles.padding(Styles.globalMargins.xsmall),
    borderColor: Styles.globalColors.black_10,
    borderRadius: Styles.borderRadius,
    borderStyle: 'solid',
    borderWidth: 1,
    padding: Styles.globalMargins.xsmall,
  },
  inputBoxSmall: {
    ...Styles.padding(Styles.globalMargins.tiny, Styles.globalMargins.xsmall),
  },
  inputContainer: {
    ...Styles.padding(0, Styles.isMobile ? Styles.globalMargins.small : Styles.globalMargins.medium),
    flex: 1,
    justifyContent: 'center',
  },
  inputPlaceholder: {
    left: 1,
    position: 'absolute',
    right: 0,
    top: 1,
  },
  invisible: {
    opacity: 0,
  },
  marginLeftAuto: {marginLeft: 'auto'},
  opacity40: {
    opacity: 0.4,
  },
  opacity75: {
    opacity: 0.75,
  },
  paddingRightTiny: {paddingRight: Styles.globalMargins.tiny},
  placeholder: {
    color: Styles.globalColors.black_40,
  },
  placeholderService: {
    color: Styles.globalColors.black_20,
  },
  positionRelative: {
    position: 'relative',
  },
  serviceIconFull: {
    height: 64,
    width: 64,
  },
  serviceIconHeaderContainer: {
    paddingTop: Styles.globalMargins.medium,
  },
  serviceProofIcon: {bottom: 0, position: 'absolute', right: 0},
  unreachableBox: Styles.platformStyles({
    common: {...Styles.padding(Styles.globalMargins.tiny, Styles.globalMargins.xsmall)},
    isElectron: {width: 360},
  }),
  warningText: {color: Styles.globalColors.brown_75, marginTop: Styles.globalMargins.small},
})

export default EnterUsername
<commit_msg>Fix invalid username permaspinner in generic proofs (#16914)<commit_after>// @flow
import * as React from 'react'
import * as Kb from '../../../common-adapters'
import * as Styles from '../../../styles'
import {SiteIcon} from '../shared'
import type {SiteIconSet} from '../../../constants/types/tracker2'

type InputProps = {|
  error: boolean,
  onChangeUsername: string => void,
  onEnterKeyDown: () => void,
  serviceIcon: SiteIconSet,
  serviceSuffix: string,
  username: string,
|}

type InputState = {|
  focus: boolean,
  username: string,
|}

class EnterUsernameInput extends React.Component<InputProps, InputState> {
  state = {focus: false, username: this.props.username}

  _onChangeUsername = username => {
    this.props.onChangeUsername(username)
    this.setState({username})
  }

  _setFocus = focus => this.setState(s => (s.focus === focus ? null : {focus}))
  _onFocus = () => this._setFocus(true)
  _onBlur = () => this._setFocus(false)

  render() {
    return (
      <Kb.Box2
        direction="vertical"
        style={Styles.collapseStyles([
          styles.inputBox,
          this.state.username && styles.inputBoxSmall,
          this.state.focus && styles.borderBlue,
          this.props.error && styles.borderRed,
        ])}
        fullWidth={true}
      >
        {!!this.state.username && (
          <Kb.Text type="BodySmallSemibold" style={styles.colorBlue}>
            Your username
          </Kb.Text>
        )}
        <Kb.Box2 direction="horizontal" gap="xtiny" alignItems="center" fullWidth={true}>
          <SiteIcon
            set={this.props.serviceIcon}
            full={false}
            style={this.state.username ? styles.opacity75 : styles.opacity40}
          />
          <Kb.Box2 direction="horizontal" style={styles.positionRelative} fullWidth={true}>
            <Kb.PlainInput
              autoFocus={true}
              flexable={true}
              textType="BodySemibold"
              value={this.state.username}
              onChangeText={this._onChangeUsername}
              onEnterKeyDown={this.props.onEnterKeyDown}
              onFocus={this._onFocus}
              onBlur={this._onBlur}
              style={styles.input}
            />
            <Kb.Box2 direction="horizontal" style={styles.inputPlaceholder} pointerEvents="none">
              <Kb.Text type="BodySemibold" lineClamp={1} style={styles.paddingRightTiny}>
                <Kb.Text
                  type="BodySemibold"
                  style={Styles.collapseStyles([
                    styles.placeholder,
                    !!this.state.username && styles.invisible,
                  ])}
                >
                  {this.state.username || 'Your username'}
                </Kb.Text>
                <Kb.Text type="BodySemibold" style={styles.placeholderService}>
                  {this.props.serviceSuffix}
                </Kb.Text>
              </Kb.Text>
            </Kb.Box2>
          </Kb.Box2>
        </Kb.Box2>
      </Kb.Box2>
    )
  }
}

const Unreachable = props => (
  <Kb.Box2
    direction="horizontal"
    gap="xtiny"
    alignItems="flex-start"
    style={Styles.collapseStyles([styles.inputBox, styles.unreachableBox])}
    fullWidth={Styles.isMobile}
  >
    <SiteIcon
      set={props.serviceIcon}
      full={false}
      style={Styles.collapseStyles([styles.opacity75, styles.inlineIcon])}
    />
    <Kb.Box2 direction="vertical" style={styles.flexOne}>
      <Kb.Text type="BodySemibold" style={styles.placeholder}>
        <Kb.Text type="BodySemibold" style={styles.colorRed}>
          {props.username}
        </Kb.Text>
        {props.serviceSuffix}
      </Kb.Text>
      <Kb.Meta title="unreachable" backgroundColor={Styles.globalColors.red} />
    </Kb.Box2>
    <Kb.Icon
      type="iconfont-proof-broken"
      color={Styles.globalColors.red}
      boxStyle={styles.marginLeftAuto}
      style={Kb.iconCastPlatformStyles(styles.inlineIcon)}
    />
  </Kb.Box2>
)

type Props = {|
  error: string,
  onBack: () => void,
  onCancel: () => void,
  onChangeUsername: string => void,
  onContinue: () => void,
  onSubmit: () => void,
  serviceIcon: SiteIconSet,
  serviceIconFull: SiteIconSet,
  serviceName: string,
  serviceSub: string,
  serviceSuffix: string,
  submitButtonLabel: string,
  unreachable: boolean,
  username: string,
  waiting: boolean, // waiting goes true -> false when proof is complete
|}

class _EnterUsername extends React.Component<Props> {
  _waitingButtonKey = 0
  componentDidUpdate(prevProps: Props) {
    if (!this.props.waiting && prevProps.waiting) {
      this.props.onContinue()
    }
    if (this.props.error && !prevProps.error) {
      // We just tried an invalid username
      // increment waiting button key so it
      // remounts and we avoid a perma-spinner
      this._waitingButtonKey++
    }
  }
  render() {
    const props = this.props
    return (
      <Kb.Box2 direction="vertical" fullWidth={true} fullHeight={true} style={styles.container}>
        {!props.unreachable && !Styles.isMobile && (
          <Kb.BackButton onClick={props.onBack} style={styles.backButton} />
        )}
        <Kb.Box2
          alignItems="center"
          direction="vertical"
          gap="xtiny"
          style={styles.serviceIconHeaderContainer}
        >
          <Kb.Box2 direction="vertical" style={styles.positionRelative}>
            <SiteIcon set={props.serviceIconFull} full={true} style={styles.serviceIconFull} />
            <Kb.Icon
              type={props.unreachable ? 'icon-proof-broken' : 'icon-proof-unfinished'}
              style={styles.serviceProofIcon}
            />
          </Kb.Box2>
          <Kb.Box2 direction="vertical" alignItems="center">
            <Kb.Text type="BodySemibold">{props.serviceName}</Kb.Text>
            <Kb.Text type="BodySmall">{props.serviceSub}</Kb.Text>
          </Kb.Box2>
        </Kb.Box2>
        <Kb.Box2
          fullWidth={true}
          direction="vertical"
          alignItems="flex-start"
          gap="xtiny"
          style={styles.inputContainer}
        >
          {props.unreachable ? (
            <Unreachable
              serviceIcon={props.serviceIcon}
              serviceSuffix={props.serviceSuffix}
              username={props.username}
            />
          ) : (
            <EnterUsernameInput
              error={!!props.error}
              serviceIcon={props.serviceIcon}
              serviceSuffix={props.serviceSuffix}
              username={props.username}
              onChangeUsername={props.onChangeUsername}
              onEnterKeyDown={props.onSubmit}
            />
          )}
          {!!props.error && <Kb.Text type="BodySmallError">{props.error}</Kb.Text>}
        </Kb.Box2>
        <Kb.Box2
          alignItems="center"
          fullWidth={true}
          direction="vertical"
          style={props.unreachable ? styles.buttonBarWarning : null}
        >
          {props.unreachable && (
            <Kb.Text type="BodySmallSemibold" center={true} style={styles.warningText}>
              You need to authorize your proof on {props.serviceName}.
            </Kb.Text>
          )}
          <Kb.ButtonBar direction="row" fullWidth={true} style={styles.buttonBar}>
            {!Styles.isMobile && !props.unreachable && (
              <Kb.Button type="Secondary" onClick={props.onBack} label="Cancel" style={styles.buttonSmall} />
            )}
            {props.unreachable ? (
              <Kb.Button
                type="PrimaryGreen"
                onClick={props.onSubmit}
                label={props.submitButtonLabel}
                style={styles.buttonBig}
              />
            ) : (
              <Kb.WaitingButton
                type="PrimaryGreen"
                onClick={props.onSubmit}
                label={props.submitButtonLabel}
                style={styles.buttonBig}
                waitingKey={null}
                key={this._waitingButtonKey}
              />
            )}
          </Kb.ButtonBar>
        </Kb.Box2>
      </Kb.Box2>
    )
  }
}
const EnterUsername = Kb.HeaderOrPopup(_EnterUsername)

const styles = Styles.styleSheetCreate({
  backButton: {left: Styles.globalMargins.small, position: 'absolute', top: Styles.globalMargins.small},
  borderBlue: {borderColor: Styles.globalColors.blue},
  borderRed: {borderColor: Styles.globalColors.red},
  buttonBar: {
    ...Styles.padding(Styles.globalMargins.small, Styles.globalMargins.medium, Styles.globalMargins.medium),
  },
  buttonBarWarning: {backgroundColor: Styles.globalColors.yellow},
  buttonBig: {flex: 2.5},
  buttonSmall: {flex: 1},
  colorBlue: {color: Styles.globalColors.blue},
  colorRed: {color: Styles.globalColors.red},
  container: Styles.platformStyles({isElectron: {height: 485, width: 560}}),
  flexOne: {flex: 1},
  inlineIcon: {
    position: 'relative',
    top: 1,
  },
  input: Styles.platformStyles({
    common: {marginRight: Styles.globalMargins.medium},
    isElectron: {
      marginTop: -1,
    },
    isMobile: {
      top: 3,
    },
  }),
  inputBox: {
    ...Styles.padding(Styles.globalMargins.xsmall),
    borderColor: Styles.globalColors.black_10,
    borderRadius: Styles.borderRadius,
    borderStyle: 'solid',
    borderWidth: 1,
    padding: Styles.globalMargins.xsmall,
  },
  inputBoxSmall: {
    ...Styles.padding(Styles.globalMargins.tiny, Styles.globalMargins.xsmall),
  },
  inputContainer: {
    ...Styles.padding(0, Styles.isMobile ? Styles.globalMargins.small : Styles.globalMargins.medium),
    flex: 1,
    justifyContent: 'center',
  },
  inputPlaceholder: {
    left: 1,
    position: 'absolute',
    right: 0,
    top: 1,
  },
  invisible: {
    opacity: 0,
  },
  marginLeftAuto: {marginLeft: 'auto'},
  opacity40: {
    opacity: 0.4,
  },
  opacity75: {
    opacity: 0.75,
  },
  paddingRightTiny: {paddingRight: Styles.globalMargins.tiny},
  placeholder: {
    color: Styles.globalColors.black_40,
  },
  placeholderService: {
    color: Styles.globalColors.black_20,
  },
  positionRelative: {
    position: 'relative',
  },
  serviceIconFull: {
    height: 64,
    width: 64,
  },
  serviceIconHeaderContainer: {
    paddingTop: Styles.globalMargins.medium,
  },
  serviceProofIcon: {bottom: 0, position: 'absolute', right: 0},
  unreachableBox: Styles.platformStyles({
    common: {...Styles.padding(Styles.globalMargins.tiny, Styles.globalMargins.xsmall)},
    isElectron: {width: 360},
  }),
  warningText: {color: Styles.globalColors.brown_75, marginTop: Styles.globalMargins.small},
})

export default EnterUsername
<|endoftext|>