text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  /**
   * @license
   * Copyright Google Inc. All Rights Reserved.
   *
   * Use of this source code is governed by an MIT-style license that can be
   * found in the LICENSE file at https://angular.io/license
   */
  const core_1 = require("@angular-devkit/core");
  const schematics_1 = require("@angular-devkit/schematics");
  const tasks_1 = require("@angular-devkit/schematics/tasks");
  const dependencies_1 = require("../utility/dependencies");
  const json_utils_1 = require("../utility/json-utils");
  const latest_versions_1 = require("../utility/latest-versions");
  const lint_fix_1 = require("../utility/lint-fix");
  const paths_1 = require("../utility/paths");
  const validation_1 = require("../utility/validation");
  const workspace_1 = require("../utility/workspace");
  const workspace_models_1 = require("../utility/workspace-models");
  const schema_1 = require("./schema");
  function addDependenciesToPackageJson(options) {
      return (host, context) => {
          [
              {
                  type: dependencies_1.NodeDependencyType.Dev,
                  name: '@angular/compiler-cli',
                  version: latest_versions_1.latestVersions.Angular,
              },
              {
                  type: dependencies_1.NodeDependencyType.Dev,
                  name: '@angular-devkit/build-angular',
                  version: latest_versions_1.latestVersions.DevkitBuildAngular,
              },
              {
                  type: dependencies_1.NodeDependencyType.Dev,
                  name: 'typescript',
                  version: latest_versions_1.latestVersions.TypeScript,
              },
          ].forEach(dependency => dependencies_1.addPackageJsonDependency(host, dependency));
          if (!options.skipInstall) {
              context.addTask(new tasks_1.NodePackageInstallTask());
          }
          return host;
      };
  }
  function readTsLintConfig(host, path) {
      const buffer = host.read(path);
      if (!buffer) {
          throw new schematics_1.SchematicsException(`Could not read ${path}.`);
      }
      const config = core_1.parseJsonAst(buffer.toString(), core_1.JsonParseMode.Loose);
      if (config.kind !== 'object') {
          throw new schematics_1.SchematicsException(`Invalid ${path}. Was expecting an object.`);
      }
      return config;
  }
  /**
   * Merges the application tslint.json with the workspace tslint.json
   * when the application being created is a root application
   *
   * @param {Tree} parentHost The root host of the schematic
   */
  function mergeWithRootTsLint(parentHost) {
      return (host) => {
          const tsLintPath = '/tslint.json';
          if (!host.exists(tsLintPath)) {
              return;
          }
          const rootTslintConfig = readTsLintConfig(parentHost, tsLintPath);
          const appTslintConfig = readTsLintConfig(host, tsLintPath);
          const recorder = host.beginUpdate(tsLintPath);
          rootTslintConfig.properties.forEach(prop => {
              if (json_utils_1.findPropertyInAstObject(appTslintConfig, prop.key.value)) {
                  // property already exists. Skip!
                  return;
              }
              json_utils_1.insertPropertyInAstObjectInOrder(recorder, appTslintConfig, prop.key.value, prop.value.value, 2);
          });
          const rootRules = json_utils_1.findPropertyInAstObject(rootTslintConfig, 'rules');
          const appRules = json_utils_1.findPropertyInAstObject(appTslintConfig, 'rules');
          if (!appRules || appRules.kind !== 'object' || !rootRules || rootRules.kind !== 'object') {
              // rules are not valid. Skip!
              return;
          }
          rootRules.properties.forEach(prop => {
              json_utils_1.insertPropertyInAstObjectInOrder(recorder, appRules, prop.key.value, prop.value.value, 4);
          });
          host.commitUpdate(recorder);
          // this shouldn't be needed but at the moment without this formatting is not correct.
          const content = readTsLintConfig(host, tsLintPath);
          host.overwrite(tsLintPath, JSON.stringify(content.value, undefined, 2));
      };
  }
  function addAppToWorkspaceFile(options, appDir) {
      let projectRoot = appDir;
      if (projectRoot) {
          projectRoot += '/';
      }
      const schematics = {};
      if (options.inlineTemplate === true
          || options.inlineStyle === true
          || options.style !== schema_1.Style.Css) {
          const componentSchematicsOptions = {};
          if (options.inlineTemplate === true) {
              componentSchematicsOptions.inlineTemplate = true;
          }
          if (options.inlineStyle === true) {
              componentSchematicsOptions.inlineStyle = true;
          }
          if (options.style && options.style !== schema_1.Style.Css) {
              componentSchematicsOptions.style = options.style;
          }
          schematics['@schematics/angular:component'] = componentSchematicsOptions;
      }
      if (options.skipTests === true) {
          ['class', 'component', 'directive', 'guard', 'module', 'pipe', 'service'].forEach((type) => {
              if (!(`@schematics/angular:${type}` in schematics)) {
                  schematics[`@schematics/angular:${type}`] = {};
              }
              schematics[`@schematics/angular:${type}`].skipTests = true;
          });
      }
      const sourceRoot = core_1.join(core_1.normalize(projectRoot), 'src');
      const project = {
          root: core_1.normalize(projectRoot),
          sourceRoot,
          projectType: workspace_models_1.ProjectType.Application,
          prefix: options.prefix || 'app',
          schematics,
          targets: {
              build: {
                  builder: workspace_models_1.Builders.Browser,
                  options: {
                      outputPath: `dist/${options.name}`,
                      index: `${sourceRoot}/index.html`,
                      main: `${sourceRoot}/main.ts`,
                      polyfills: `${sourceRoot}/polyfills.ts`,
                      tsConfig: `${projectRoot}tsconfig.app.json`,
                      aot: !!options.enableIvy,
                      assets: [
                          `${sourceRoot}/favicon.ico`,
                          `${sourceRoot}/assets`,
                      ],
                      styles: [
                          `${sourceRoot}/styles.${options.style}`,
                      ],
                      scripts: [],
                  },
                  configurations: {
                      production: {
                          fileReplacements: [{
                                  replace: `${sourceRoot}/environments/environment.ts`,
                                  with: `${sourceRoot}/environments/environment.prod.ts`,
                              }],
                          optimization: true,
                          outputHashing: 'all',
                          sourceMap: false,
                          extractCss: true,
                          namedChunks: false,
                          aot: true,
                          extractLicenses: true,
                          vendorChunk: false,
                          buildOptimizer: true,
                          budgets: [{
                                  type: 'initial',
                                  maximumWarning: '2mb',
                                  maximumError: '5mb',
                              }],
                      },
                  },
              },
              serve: {
                  builder: workspace_models_1.Builders.DevServer,
                  options: {
                      browserTarget: `${options.name}:build`,
                  },
                  configurations: {
                      production: {
                          browserTarget: `${options.name}:build:production`,
                      },
                  },
              },
              'extract-i18n': {
                  builder: workspace_models_1.Builders.ExtractI18n,
                  options: {
                      browserTarget: `${options.name}:build`,
                  },
              },
              test: options.minimal ? undefined : {
                  builder: workspace_models_1.Builders.Karma,
                  options: {
                      main: `${sourceRoot}/test.ts`,
                      polyfills: `${sourceRoot}/polyfills.ts`,
                      tsConfig: `${projectRoot}tsconfig.spec.json`,
                      karmaConfig: `${projectRoot}karma.conf.js`,
                      assets: [
                          `${sourceRoot}/favicon.ico`,
                          `${sourceRoot}/assets`,
                      ],
                      styles: [
                          `${sourceRoot}/styles.${options.style}`,
                      ],
                      scripts: [],
                  },
              },
              lint: {
                  builder: workspace_models_1.Builders.TsLint,
                  options: {
                      tsConfig: [
                          `${projectRoot}tsconfig.app.json`,
                          `${projectRoot}tsconfig.spec.json`,
                      ],
                      exclude: [
                          '**/node_modules/**',
                      ],
                  },
              },
          },
      };
      return workspace_1.updateWorkspace(workspace => {
          if (workspace.projects.size === 0) {
              workspace.extensions.defaultProject = options.name;
          }
          workspace.projects.add({
              name: options.name,
              ...project,
          });
      });
  }
  function minimalPathFilter(path) {
      const toRemoveList = /(test.ts|tsconfig.spec.json|karma.conf.js|tslint.json).template$/;
      return !toRemoveList.test(path);
  }
  function default_1(options) {
      return async (host, context) => {
          if (!options.name) {
              throw new schematics_1.SchematicsException(`Invalid options, "name" is required.`);
          }
          validation_1.validateProjectName(options.name);
          options.prefix = options.prefix || 'app';
          const appRootSelector = `${options.prefix}-root`;
          const componentOptions = !options.minimal ?
              {
                  inlineStyle: options.inlineStyle,
                  inlineTemplate: options.inlineTemplate,
                  skipTests: options.skipTests,
                  style: options.style,
                  viewEncapsulation: options.viewEncapsulation,
              } :
              {
                  inlineStyle: true,
                  inlineTemplate: true,
                  skipTests: true,
                  style: options.style,
              };
          const workspace = await workspace_1.getWorkspace(host);
          const newProjectRoot = workspace.extensions.newProjectRoot || '';
          const isRootApp = options.projectRoot !== undefined;
          const appDir = isRootApp
              ? options.projectRoot
              : core_1.join(core_1.normalize(newProjectRoot), options.name);
          const sourceDir = `${appDir}/src/app`;
          const e2eOptions = {
              relatedAppName: options.name,
              rootSelector: appRootSelector,
          };
          return schematics_1.chain([
              addAppToWorkspaceFile(options, appDir),
              schematics_1.mergeWith(schematics_1.apply(schematics_1.url('./files'), [
                  options.minimal ? schematics_1.filter(minimalPathFilter) : schematics_1.noop(),
                  schematics_1.applyTemplates({
                      utils: core_1.strings,
                      ...options,
                      relativePathToWorkspaceRoot: paths_1.relativePathToWorkspaceRoot(appDir),
                      appName: options.name,
                      isRootApp,
                  }),
                  isRootApp ? mergeWithRootTsLint(host) : schematics_1.noop(),
                  schematics_1.move(appDir),
              ]), schematics_1.MergeStrategy.Overwrite),
              schematics_1.schematic('module', {
                  name: 'app',
                  commonModule: false,
                  flat: true,
                  routing: options.routing,
                  routingScope: 'Root',
                  path: sourceDir,
                  project: options.name,
              }),
              schematics_1.schematic('component', {
                  name: 'app',
                  selector: appRootSelector,
                  flat: true,
                  path: sourceDir,
                  skipImport: true,
                  project: options.name,
                  ...componentOptions,
              }),
              schematics_1.mergeWith(schematics_1.apply(schematics_1.url('./other-files'), [
                  componentOptions.inlineTemplate
                      ? schematics_1.filter(path => !path.endsWith('.html.template'))
                      : schematics_1.noop(),
                  componentOptions.skipTests
                      ? schematics_1.filter(path => !path.endsWith('.spec.ts.template'))
                      : schematics_1.noop(),
                  schematics_1.applyTemplates({
                      utils: core_1.strings,
                      ...options,
                      selector: appRootSelector,
                      ...componentOptions,
                  }),
                  schematics_1.move(sourceDir),
              ]), schematics_1.MergeStrategy.Overwrite),
              options.minimal ? schematics_1.noop() : schematics_1.schematic('e2e', e2eOptions),
              options.skipPackageJson ? schematics_1.noop() : addDependenciesToPackageJson(options),
              options.lintFix ? lint_fix_1.applyLintFix(appDir) : schematics_1.noop(),
          ]);
      };
  }
  exports.default = default_1;
  