<commit_before>import { MetadataGroup, validators } from '@ionic/cli-framework';
import { columnar, prettyPath } from '@ionic/cli-framework/utils/format';
import { isValidURL, slugify } from '@ionic/cli-framework/utils/string';
import { mkdir, pathExists, remove, unlink } from '@ionic/utils-fs';
import * as Debug from 'debug';
import * as path from 'path';

import { PROJECT_FILE } from '../constants';
import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun, IProject, IShellRunOptions, ProjectType, ResolvedStarterTemplate, StarterManifest } from '../definitions';
import { failure, input, strong } from '../lib/color';
import { Command } from '../lib/command';
import { FatalException } from '../lib/errors';
import { runCommand } from '../lib/executor';
import { createProjectFromDetails, createProjectFromDirectory, isValidProjectId } from '../lib/project';
import { promptToSignup } from '../lib/session';
import { prependNodeModulesBinToPath } from '../lib/shell';
import { AppSchema, STARTER_BASE_URL, STARTER_TEMPLATES, SUPPORTED_FRAMEWORKS, getAdvertisement, getStarterList, getStarterProjectTypes, readStarterManifest, verifyOptions } from '../lib/start';
import { emoji } from '../lib/utils/emoji';
import { createRequest } from '../lib/utils/http';

const debug = Debug('ionic:commands:start');

interface StartWizardApp {
  type: ProjectType;
  name: string;
  appId: string;
  template: string;
  'package-id': string;
  tid: string;
  email: string;
  theme: string;
  ip: string;
  utm: { [key: string]: string };
}

export class StartCommand extends Command implements CommandPreRun {
  private canRemoveExisting = false;

  private schema?: AppSchema;

  async getMetadata(): Promise<CommandMetadata> {
    return {
      name: 'start',
      type: 'global',
      summary: 'Create a new project',
      description: `
This command creates a working Ionic app. It installs dependencies for you and sets up your project.

Running ${input('ionic start')} without any arguments will prompt you for information about your new project.

The first argument is your app's ${input('name')}. Don't worry--you can always change this later. The ${input('--project-id')} is generated from ${input('name')} unless explicitly specified.

The second argument is the ${input('template')} from which to generate your app. You can list all templates with the ${input('--list')} option. You can also specify a git repository URL for ${input('template')}, in which case the existing project will be cloned.

Use the ${input('--type')} option to start projects using older versions of Ionic. For example, you can start an Ionic 3 project with ${input('--type=ionic-angular')}. Use ${input('--list')} to see all project types and templates.
      `,
      exampleCommands: [
        '',
        '--list',
        'myApp',
        'myApp blank',
        'myApp tabs --cordova',
        'myApp tabs --capacitor',
        'myApp super --type=ionic-angular',
        'myApp blank --type=ionic1',
        'cordovaApp tabs --cordova',
        '"My App" blank',
        '"Conference App" https://github.com/ionic-team/ionic-conference-app',
      ],
      inputs: [
        {
          name: 'name',
          summary: `The name of your new project (e.g. ${input('myApp')}, ${input('"My App"')})`,
          validators: [validators.required],
        },
        {
          name: 'template',
          summary: `The starter template to use (e.g. ${['blank', 'tabs'].map(t => input(t)).join(', ')}; use ${input('--list')} to see all)`,
          validators: [validators.required],
        },
      ],
      options: [
        {
          name: 'list',
          summary: 'List available starter templates',
          type: Boolean,
          aliases: ['l'],
        },
        {
          name: 'type',
          summary: `Type of project to start (e.g. ${getStarterProjectTypes().map(type => input(type)).join(', ')})`,
          type: String,
        },
        {
          name: 'cordova',
          summary: 'Include Cordova integration',
          type: Boolean,
        },
        {
          name: 'capacitor',
          summary: 'Include Capacitor integration',
          type: Boolean,
          groups: [MetadataGroup.EXPERIMENTAL],
        },
        {
          name: 'deps',
          summary: 'Do not install npm/yarn dependencies',
          type: Boolean,
          default: true,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'git',
          summary: 'Do not initialize a git repo',
          type: Boolean,
          default: true,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'link',
          summary: 'Connect your new app to Ionic',
          type: Boolean,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'id',
          summary: 'Specify an Ionic App ID to link',
        },
        {
          name: 'project-id',
          summary: 'Specify a slug for your app (used for the directory name and package name)',
          groups: [MetadataGroup.ADVANCED],
          spec: { value: 'slug' },
        },
        {
          name: 'package-id',
          summary: 'Specify the bundle ID/application ID for your app (reverse-DNS notation)',
          groups: [MetadataGroup.ADVANCED],
          spec: { value: 'id' },
        },
        {
          name: 'start-id',
          summary: 'Used by the Ionic app start experience to generate an associated app locally',
          groups: [MetadataGroup.HIDDEN],
          spec: { value: 'id' },
        },
        {
          name: 'tag',
          summary: `Specify a tag to use for the starters (e.g. ${['latest', 'testing', 'next'].map(t => input(t)).join(', ')})`,
          default: 'latest',
          groups: [MetadataGroup.HIDDEN],
        },
      ],
    };
  }

  async startIdStart(inputs: CommandLineInputs, options: CommandLineOptions) {
    const startId = options['start-id'];

    const wizardApiUrl = process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`;

    const { req } = await createRequest('GET', `${wizardApiUrl}/api/v1/wizard/app/${startId}`, this.env.config.getHTTPConfig());

    const data = (await req).body as StartWizardApp;

    let projectDir = slugify(data.name);<commit_msg>chore(start): add logging for online start experience (#4354)<commit_after>import { MetadataGroup, validators } from '@ionic/cli-framework';
import { columnar, prettyPath } from '@ionic/cli-framework/utils/format';
import { isValidURL, slugify } from '@ionic/cli-framework/utils/string';
import { mkdir, pathExists, remove, unlink } from '@ionic/utils-fs';
import * as chalk from 'chalk';
import * as Debug from 'debug';
import * as path from 'path';

import { PROJECT_FILE } from '../constants';
import { CommandInstanceInfo, CommandLineInputs, CommandLineOptions, CommandMetadata, CommandPreRun, IProject, IShellRunOptions, ProjectType, ResolvedStarterTemplate, StarterManifest } from '../definitions';
import { failure, input, strong } from '../lib/color';
import { Command } from '../lib/command';
import { FatalException } from '../lib/errors';
import { runCommand } from '../lib/executor';
import { createProjectFromDetails, createProjectFromDirectory, isValidProjectId } from '../lib/project';
import { promptToSignup } from '../lib/session';
import { prependNodeModulesBinToPath } from '../lib/shell';
import { AppSchema, STARTER_BASE_URL, STARTER_TEMPLATES, SUPPORTED_FRAMEWORKS, getAdvertisement, getStarterList, getStarterProjectTypes, readStarterManifest, verifyOptions } from '../lib/start';
import { emoji } from '../lib/utils/emoji';
import { createRequest } from '../lib/utils/http';

const debug = Debug('ionic:commands:start');

interface StartWizardApp {
  type: ProjectType;
  name: string;
  appId: string;
  template: string;
  'package-id': string;
  tid: string;
  email: string;
  theme: string;
  ip: string;
  utm: { [key: string]: string };
}

export class StartCommand extends Command implements CommandPreRun {
  private canRemoveExisting = false;

  private schema?: AppSchema;

  async getMetadata(): Promise<CommandMetadata> {
    return {
      name: 'start',
      type: 'global',
      summary: 'Create a new project',
      description: `
This command creates a working Ionic app. It installs dependencies for you and sets up your project.

Running ${input('ionic start')} without any arguments will prompt you for information about your new project.

The first argument is your app's ${input('name')}. Don't worry--you can always change this later. The ${input('--project-id')} is generated from ${input('name')} unless explicitly specified.

The second argument is the ${input('template')} from which to generate your app. You can list all templates with the ${input('--list')} option. You can also specify a git repository URL for ${input('template')}, in which case the existing project will be cloned.

Use the ${input('--type')} option to start projects using older versions of Ionic. For example, you can start an Ionic 3 project with ${input('--type=ionic-angular')}. Use ${input('--list')} to see all project types and templates.
      `,
      exampleCommands: [
        '',
        '--list',
        'myApp',
        'myApp blank',
        'myApp tabs --cordova',
        'myApp tabs --capacitor',
        'myApp super --type=ionic-angular',
        'myApp blank --type=ionic1',
        'cordovaApp tabs --cordova',
        '"My App" blank',
        '"Conference App" https://github.com/ionic-team/ionic-conference-app',
      ],
      inputs: [
        {
          name: 'name',
          summary: `The name of your new project (e.g. ${input('myApp')}, ${input('"My App"')})`,
          validators: [validators.required],
        },
        {
          name: 'template',
          summary: `The starter template to use (e.g. ${['blank', 'tabs'].map(t => input(t)).join(', ')}; use ${input('--list')} to see all)`,
          validators: [validators.required],
        },
      ],
      options: [
        {
          name: 'list',
          summary: 'List available starter templates',
          type: Boolean,
          aliases: ['l'],
        },
        {
          name: 'type',
          summary: `Type of project to start (e.g. ${getStarterProjectTypes().map(type => input(type)).join(', ')})`,
          type: String,
        },
        {
          name: 'cordova',
          summary: 'Include Cordova integration',
          type: Boolean,
        },
        {
          name: 'capacitor',
          summary: 'Include Capacitor integration',
          type: Boolean,
          groups: [MetadataGroup.EXPERIMENTAL],
        },
        {
          name: 'deps',
          summary: 'Do not install npm/yarn dependencies',
          type: Boolean,
          default: true,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'git',
          summary: 'Do not initialize a git repo',
          type: Boolean,
          default: true,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'link',
          summary: 'Connect your new app to Ionic',
          type: Boolean,
          groups: [MetadataGroup.ADVANCED],
        },
        {
          name: 'id',
          summary: 'Specify an Ionic App ID to link',
        },
        {
          name: 'project-id',
          summary: 'Specify a slug for your app (used for the directory name and package name)',
          groups: [MetadataGroup.ADVANCED],
          spec: { value: 'slug' },
        },
        {
          name: 'package-id',
          summary: 'Specify the bundle ID/application ID for your app (reverse-DNS notation)',
          groups: [MetadataGroup.ADVANCED],
          spec: { value: 'id' },
        },
        {
          name: 'start-id',
          summary: 'Used by the Ionic app start experience to generate an associated app locally',
          groups: [MetadataGroup.HIDDEN],
          spec: { value: 'id' },
        },
        {
          name: 'tag',
          summary: `Specify a tag to use for the starters (e.g. ${['latest', 'testing', 'next'].map(t => input(t)).join(', ')})`,
          default: 'latest',
          groups: [MetadataGroup.HIDDEN],
        },
      ],
    };
  }

  async startIdStart(inputs: CommandLineInputs, options: CommandLineOptions) {
    const startId = options['start-id'];

    const wizardApiUrl = process.env.START_WIZARD_URL_BASE || `https://ionicframework.com`;

    const { req } = await createRequest('GET', `${wizardApiUrl}/api/v1/wizard/app/${startId}`, this.env.config.getHTTPConfig());

    const error = (e?: Error) => {
      this.env.log.error(`No such app ${chalk.bold(startId)}. This app configuration may have expired. Please retry at https://ionicframework.com/start`);
      if (e) {
        throw e;
      }
    };

    let data: StartWizardApp;
    try {
      const ret = await req;
      if (ret.status !== 200) {
        return error();
      }

      data = (await req).body as StartWizardApp;

      if (!data) {
        return error();
      }
    } catch (e) {
      return error(e);
    }

    let projectDir = slugify(data.name);
{'fraction_non_alphanumeric': 0.1366356131182307, 'fraction_numerical': 0.000984624706506097, 'mean_word_length': 2.5304812834224597, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 12, 'https://': 5, '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': '1999107', 'n_tokens_mistral': 3792, 'n_tokens_neox': 3717, 'n_words': 1145}