docs: function doGenerate(swagger, options) {
    if (!options.templates) {
      options.templates = path.join(__dirname, 'templates');
    }
  
    var output = path.normalize(options.output || 'src/app/api');
    var prefix = options.prefix || 'Api';
  
    if (swagger.swagger !== '2.0') {
      console.error(
        'Invalid swagger specification. Must be a 2.0. Currently ' +
          swagger.swagger
      );
      process.exit(1);
    }
    swagger.paths = swagger.paths || {};
    swagger.models = swagger.models || [];
    var models = processModels(swagger, options);
    var services = processServices(swagger, models, options);
  
    // Apply the tag filter. If includeTags is null, uses all services,
    // but still can remove unused models
    const includeTags = options.includeTags;
    if (typeof includeTags == 'string') {
      options.includeTags = includeTags.split(',');
    }
    const excludeTags = options.excludeTags;
    if (typeof excludeTags == 'string') {
      options.excludeTags = excludeTags.split(',');
    }
    applyTagFilter(models, services, options);
  
    // Read the templates
    var templates = {};
    var files = fs.readdirSync(options.templates);
    files.forEach(function(file, index) {
      var pos = file.indexOf('.mustache');
      if (pos >= 0) {
        var fullFile = path.join(options.templates, file);
        templates[file.substr(0, pos)] = fs.readFileSync(fullFile, 'utf-8');
      }
    });
  
    // Prepare the output folder
    const modelsOutput = path.join(output, 'models');
    const servicesOutput = path.join(output, 'services');
    mkdirs(modelsOutput);
    mkdirs(servicesOutput);
  
    var removeStaleFiles = options.removeStaleFiles !== false;
    var generateEnumModule = options.enumModule !== false;
  
    // Utility function to render a template and write it to a file
    var generate = function(template, model, file) {
      var code = Mustache.render(template, model, templates)
        .replace(/[^\S\r\n]+$/gm, '');
      fs.writeFileSync(file, code, 'UTF-8');
      console.info('Wrote ' + file);
    };
  
    // Calculate the globally used names
    var moduleClass = toClassName(prefix + 'Module');
    var moduleFile = toFileName(moduleClass);
    // Angular's best practices demands xxx.module.ts, not xxx-module.ts
    moduleFile = moduleFile.replace(/\-module$/, '.module');
    var configurationClass = toClassName(prefix + 'Configuration');
    var configurationInterface = toClassName(prefix + 'ConfigurationInterface');
    var configurationFile = toFileName(configurationClass);
  
    function applyGlobals(to) {
      to.prefix = prefix;
      to.moduleClass = moduleClass;
      to.moduleFile = moduleFile;
      to.configurationClass = configurationClass;
      to.configurationInterface = configurationInterface;
      to.configurationFile = configurationFile;
      return to;
    }
  
    // Write the models and examples
    var modelsArray = [];
    for (var modelName in models) {
      var model = models[modelName];
      if (model.modelIsEnum) {
        model.enumModule = generateEnumModule;
      }
      applyGlobals(model);
  
      // When the model name differs from the class name, it will be duplicated
      // in the array. For example the-user would be TheUser, and would be twice.
      if (modelsArray.includes(model)) {
        continue;
      }
      modelsArray.push(model);
      generate(
        templates.model,
        model,
        path.join(modelsOutput, model.modelFile + '.ts')
      );
      if (options.generateExamples && model.modelExample) {
        var example = JSON.stringify(model.modelExample, null, 2);
        example = example.replace(/'/g, "\\'");
        example = example.replace(/"/g, "'");
        example = example.replace(/\n/g, "\n  ");
        model.modelExampleStr = example;
        generate(
          templates.example,
          model,
          path.join(modelsOutput, model.modelExampleFile + '.ts')
        );
      }
    }
    if (modelsArray.length > 0) {
      modelsArray[modelsArray.length - 1].modelIsLast = true;
    }
    if (removeStaleFiles) {
      var modelFiles = fs.readdirSync(modelsOutput);
      modelFiles.forEach((file, index) => {
        var ok = false;
        var basename = path.basename(file);
        for (var modelName in models) {
          var model = models[modelName];
          if (basename == model.modelFile + '.ts'
            || basename == model.modelExampleFile + '.ts'
              && model.modelExampleStr != null) {
            ok = true;
            break;
          }
        }
        if (!ok) {
          rmIfExists(path.join(modelsOutput, file));
        }
      });
    }
  
    // Write the model index
    var modelIndexFile = path.join(output, 'models.ts');
    if (options.modelIndex !== false) {
      generate(templates.models, { models: modelsArray }, modelIndexFile);
    } else if (removeStaleFiles) {
      rmIfExists(modelIndexFile);
    }
  
    // Write the StrictHttpResponse type
    generate(templates.strictHttpResponse, {},
      path.join(output, 'strict-http-response.ts'));
  
    // Write the services
    var servicesArray = [];
    for (var serviceName in services) {
      var service = services[serviceName];
      service.generalErrorHandler = options.errorHandler !== false;
      applyGlobals(service);
      servicesArray.push(service);
  
      generate(
        templates.service,
        service,
        path.join(servicesOutput, service.serviceFile + '.ts')
      );
    }
    if (servicesArray.length > 0) {
      servicesArray[servicesArray.length - 1].serviceIsLast = true;
    }
    if (removeStaleFiles) {
      var serviceFiles = fs.readdirSync(servicesOutput);
      serviceFiles.forEach((file, index) => {
        var ok = false;
        var basename = path.basename(file);
        for (var serviceName in services) {
          var service = services[serviceName];
          if (basename == service.serviceFile + '.ts') {
            ok = true;
            break;
          }
        }
        if (!ok) {
          rmIfExists(path.join(servicesOutput, file));
        }
      });
    }
  
    // Write the service index
    var serviceIndexFile = path.join(output, 'services.ts');
    if (options.serviceIndex !== false) {
      generate(templates.services, { services: servicesArray }, serviceIndexFile);
    } else if (removeStaleFiles) {
      rmIfExists(serviceIndexFile);
    }
  
    // Write the module
    var fullModuleFile = path.join(output, moduleFile + '.ts');
    if (options.apiModule !== false) {
      generate(templates.module, applyGlobals({
          services: servicesArray
        }),
        fullModuleFile);
    } else if (removeStaleFiles) {
      rmIfExists(fullModuleFile);
    }
  
    // Write the configuration
    {
      var rootUrl = '';
      if (swagger.hasOwnProperty('host') && swagger.host !== '') {
        var schemes = swagger.schemes || [];
        var scheme = schemes.length === 0 ? '//' : schemes[0] + '://';
        rootUrl = scheme + swagger.host;
      }
      if (swagger.hasOwnProperty('basePath') && swagger.basePath !== ''
        && swagger.basePath !== '/') {
        rootUrl += swagger.basePath;
      }
  
      generate(templates.configuration, applyGlobals({
          rootUrl: rootUrl,
        }),
        path.join(output, configurationFile + '.ts')
      );
    }
  
    // Write the BaseService
    {
      generate(templates.baseService, applyGlobals({}),
        path.join(output, 'base-service.ts'));
    }
  }