<commit_before>/* disable this rule _here_ to avoid conflict with ongoing changes */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import ms from 'ms';
import bytes from 'bytes';
import { tmpdir } from 'os';
import { dirname, join, relative } from 'path';
import { fork, ChildProcess } from 'child_process';
import { readFile, mkdirp } from 'fs-extra';
import ignore, { Ignore } from '@zeit/dockerignore';
import { createFunction, initializeRuntime } from '@zeit/fun';
import { File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
import stripAnsi from 'strip-ansi';
import chalk from 'chalk';
import ora from 'ora';

import { globBuilderInputs } from './glob';
import DevServer from './dev-server';
import IGNORED from '../../../util/ignored';
import { Output } from '../../../util/output';
import { LambdaSizeExceededError } from '../../../util/errors-ts';
import { builderModulePathPromise, getBuilder } from './builder-cache';
import {
  EnvConfig,
  NowConfig,
  BuildMatch,
  BuildResult,
  BuilderInputs,
  BuilderOutput,
  BuilderOutputs
} from './types';

interface BuildMessage {
  type: string;
};

interface BuildMessageResult extends BuildMessage {
  type: 'buildResult';
  result?: BuilderOutputs | BuildResult;
  error?: object;
};

const tmpDir = tmpdir();
const getWorkPath = () =>
  join(
    tmpDir,
    'co.zeit.now',
    'dev',
    'workPaths',
    Math.random()
      .toString(32)
      .slice(-8)
  );

const isLogging = new WeakSet<ChildProcess>();

let nodeBinPromise: Promise<string>;

async function getNodeBin(): Promise<string> {
  const runtime = await initializeRuntime('nodejs8.10');
  if (!runtime.cacheDir) {
    throw new Error('nodejs8.10 runtime failed to initialize');
  }
  const nodeBin = join(runtime.cacheDir, 'bin', 'node');
  return nodeBin;
}

function pipeChildLogging(child: ChildProcess): void {
  if (!isLogging.has(child)) {
    child.stdout!.pipe(process.stdout);
    child.stderr!.pipe(process.stderr);
    isLogging.add(child);
  }
}

async function createBuildProcess(
  match: BuildMatch,
  buildEnv: EnvConfig,
  output: Output
): Promise<ChildProcess> {
  if (!nodeBinPromise) {
    nodeBinPromise = getNodeBin();
  }
  const [execPath, modulePath] = await Promise.all([
    nodeBinPromise,
    builderModulePathPromise
  ]);
  const buildProcess = fork(modulePath, [], {
    cwd: match.workPath,
    env: {
      ...process.env,
      PATH: `${dirname(execPath)}:${process.env.PATH}`,
      ...buildEnv
    },
    execPath,
    execArgv: [],
    stdio: ['ignore', 'pipe', 'pipe', 'ipc']
  });
  match.buildProcess = buildProcess;

  buildProcess.on('message', m => {
    // console.log('got message from builder:', m);
  });
  buildProcess.on('exit', (code, signal) => {
    output.debug(
      `Build process for ${match.src} exited with ${signal || code}`
    );
    match.buildProcess = undefined;
  });

  buildProcess.stdout!.setEncoding('utf8');
  buildProcess.stderr!.setEncoding('utf8');

  return new Promise((resolve, reject) => {
    // The first message that the builder process sends is the `ready` event
    buildProcess.once('message', ({ type }) => {
      if (type !== 'ready') {
        reject(new Error('Did not get "ready" event from builder'));
      } else {
        resolve(buildProcess);
      }
    });
  });
}

export async function executeBuild(
  nowJson: NowConfig,
  devServer: DevServer,
  files: BuilderInputs,
  match: BuildMatch,
  requestPath: string | null,
  isInitialBuild: boolean,
  filesChanged?: string[],
  filesRemoved?: string[]
): Promise<void> {
  const {
    builderWithPkg: { runInProcess, builder, package: pkg }
  } = match;
  const { env } = devServer;
  const { src: entrypoint, workPath } = match;
  await mkdirp(workPath);

  const startTime = Date.now();

  if (match.use !== '@now/static') {
    devServer.output.debug(
      `${pkg.version ? `v${pkg.version} ` : ''}(workPath = ${workPath})`
    );
  }

  const builderConfig = builder.config || {};
  const config = match.config || {};

  let result: BuildResult;

  let { buildProcess } = match;
  if (!runInProcess && !buildProcess) {
    devServer.output.debug(`Creating build process for ${entrypoint}`);
    buildProcess = await createBuildProcess(
      match,
      devServer.buildEnv,
      devServer.output
    );
  }

  const buildParams = {
    files,
    entrypoint,
    workPath,
    config,
    meta: { isDev: true, requestPath, filesChanged, filesRemoved }
  };

  let buildResultOrOutputs: BuilderOutputs | BuildResult;
  if (buildProcess) {
    let spinLogger;

    if (isInitialBuild && process.stdout.isTTY) {
      const logTitle = `${chalk.bold(`Setting up Builder for ${chalk.underline(entrypoint)}`)}:`;
      const fullLogs: string[] = [];
      const spinner = ora(logTitle).start();

      buildProcess!.on('message', ({ type }) => {
        if (type === 'buildResult') {
          spinner.stop();
        }
      });
<commit_msg>[now dev] Don't render initial builds spinner when `debug` is enabled (#2271)<commit_after>/* disable this rule _here_ to avoid conflict with ongoing changes */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import ms from 'ms';
import bytes from 'bytes';
import { tmpdir } from 'os';
import { dirname, join, relative } from 'path';
import { fork, ChildProcess } from 'child_process';
import { readFile, mkdirp } from 'fs-extra';
import ignore, { Ignore } from '@zeit/dockerignore';
import { createFunction, initializeRuntime } from '@zeit/fun';
import { File, Lambda, FileBlob, FileFsRef } from '@now/build-utils';
import stripAnsi from 'strip-ansi';
import chalk from 'chalk';
import ora from 'ora';

import { globBuilderInputs } from './glob';
import DevServer from './dev-server';
import IGNORED from '../../../util/ignored';
import { Output } from '../../../util/output';
import { LambdaSizeExceededError } from '../../../util/errors-ts';
import { builderModulePathPromise, getBuilder } from './builder-cache';
import {
  EnvConfig,
  NowConfig,
  BuildMatch,
  BuildResult,
  BuilderInputs,
  BuilderOutput,
  BuilderOutputs
} from './types';

interface BuildMessage {
  type: string;
};

interface BuildMessageResult extends BuildMessage {
  type: 'buildResult';
  result?: BuilderOutputs | BuildResult;
  error?: object;
};

const tmpDir = tmpdir();
const getWorkPath = () =>
  join(
    tmpDir,
    'co.zeit.now',
    'dev',
    'workPaths',
    Math.random()
      .toString(32)
      .slice(-8)
  );

const isLogging = new WeakSet<ChildProcess>();

let nodeBinPromise: Promise<string>;

async function getNodeBin(): Promise<string> {
  const runtime = await initializeRuntime('nodejs8.10');
  if (!runtime.cacheDir) {
    throw new Error('nodejs8.10 runtime failed to initialize');
  }
  const nodeBin = join(runtime.cacheDir, 'bin', 'node');
  return nodeBin;
}

function pipeChildLogging(child: ChildProcess): void {
  if (!isLogging.has(child)) {
    child.stdout!.pipe(process.stdout);
    child.stderr!.pipe(process.stderr);
    isLogging.add(child);
  }
}

async function createBuildProcess(
  match: BuildMatch,
  buildEnv: EnvConfig,
  output: Output
): Promise<ChildProcess> {
  if (!nodeBinPromise) {
    nodeBinPromise = getNodeBin();
  }
  const [execPath, modulePath] = await Promise.all([
    nodeBinPromise,
    builderModulePathPromise
  ]);
  const buildProcess = fork(modulePath, [], {
    cwd: match.workPath,
    env: {
      ...process.env,
      PATH: `${dirname(execPath)}:${process.env.PATH}`,
      ...buildEnv
    },
    execPath,
    execArgv: [],
    stdio: ['ignore', 'pipe', 'pipe', 'ipc']
  });
  match.buildProcess = buildProcess;

  buildProcess.on('message', m => {
    // console.log('got message from builder:', m);
  });
  buildProcess.on('exit', (code, signal) => {
    output.debug(
      `Build process for ${match.src} exited with ${signal || code}`
    );
    match.buildProcess = undefined;
  });

  buildProcess.stdout!.setEncoding('utf8');
  buildProcess.stderr!.setEncoding('utf8');

  return new Promise((resolve, reject) => {
    // The first message that the builder process sends is the `ready` event
    buildProcess.once('message', ({ type }) => {
      if (type !== 'ready') {
        reject(new Error('Did not get "ready" event from builder'));
      } else {
        resolve(buildProcess);
      }
    });
  });
}

export async function executeBuild(
  nowJson: NowConfig,
  devServer: DevServer,
  files: BuilderInputs,
  match: BuildMatch,
  requestPath: string | null,
  isInitialBuild: boolean,
  filesChanged?: string[],
  filesRemoved?: string[]
): Promise<void> {
  const {
    builderWithPkg: { runInProcess, builder, package: pkg }
  } = match;
  const { env } = devServer;
  const { src: entrypoint, workPath } = match;
  await mkdirp(workPath);

  const startTime = Date.now();

  if (match.use !== '@now/static') {
    devServer.output.debug(
      `${pkg.version ? `v${pkg.version} ` : ''}(workPath = ${workPath})`
    );
  }

  const builderConfig = builder.config || {};
  const config = match.config || {};

  let result: BuildResult;

  let { buildProcess } = match;
  if (!runInProcess && !buildProcess) {
    devServer.output.debug(`Creating build process for ${entrypoint}`);
    buildProcess = await createBuildProcess(
      match,
      devServer.buildEnv,
      devServer.output
    );
  }

  const buildParams = {
    files,
    entrypoint,
    workPath,
    config,
    meta: { isDev: true, requestPath, filesChanged, filesRemoved }
  };

  let buildResultOrOutputs: BuilderOutputs | BuildResult;
  if (buildProcess) {
    let spinLogger;

    if (isInitialBuild && !devServer.debug && process.stdout.isTTY) {
      const logTitle = `${chalk.bold(`Setting up Builder for ${chalk.underline(entrypoint)}`)}:`;
      const fullLogs: string[] = [];
      const spinner = ora(logTitle).start();

      buildProcess!.on('message', ({ type }) => {
        if (type === 'buildResult') {
          spinner.stop();
        }
      });

{'fraction_non_alphanumeric': 0.150501002004008, 'fraction_numerical': 0.0026052104208416833, 'mean_word_length': 3.4858426966292133, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3813411', 'n_tokens_mistral': 3113, 'n_tokens_neox': 3072, 'n_words': 782}