text: #!/usr/bin/env node
  
  'use strict'
  
  const config = require('./lib/config.js');
  const loader = require('./lib/loader.js');
  const linter = require('oas-linter');
  const rules = require('./lib/rules.js');
  const validator = require('oas-validator');
  const fromJsonSchema = require('json-schema-to-openapi-schema');
  
  const colors = process.env.NODE_DISABLE_COLORS ? {} : {
      red: '\x1b[31m',
      green: '\x1b[32m',
      yellow: '\x1b[33m',
      blue: '\x1b[34m',
      magenta: '\x1b[35m',
      cyan: '\x1b[36m',
      white: '\x1b[37m',
      reset: '\x1b[0m'
  };
  
  const formatSchemaError = (err, context) => {
      const pointer = context.pop();
      let output = `
  ${colors.yellow + pointer}
  `;
  
      if (err.name === 'AssertionError') {
          output += colors.reset + truncateLongMessages(err.message);
      }
      else if (err instanceof validator.CLIError) {
          output += colors.reset + err.message;
      }
      else {
          output += colors.red + err.stack;
      }
      return output;
  }
  
  const truncateLongMessages = message => {
      let lines = message.split('\n');
      if (lines.length > 6) {
          lines = lines.slice(0, 5).concat(
              ['  ... snip ...'],
              lines.slice(-1)
          );
      }
      return lines.join('\n');
  }
  
  const formatLintResults = lintResults => {
      let output = '';
      lintResults.forEach(result => {
          const { rule, error, pointer } = result;
  
          output += `
  ${colors.yellow + pointer} ${colors.cyan} R: ${rule.name} ${colors.white} D: ${rule.description}
  ${colors.reset + truncateLongMessages(error.message)}
  
  More information: ${rule.url}#${rule.name}
  `;
      });
  
      return output;
  }
  
  const command = async (specFile, cmd) => {
      config.init(cmd);
      const jsonSchema = config.get('jsonSchema');
      const verbose = config.get('quiet') ? 0 : config.get('verbose', 1);
      const rulesets = config.get('lint:rules');
      const skip = config.get('lint:skip');
  
      rules.init({
          skip
      });
      await loader.loadRulesets(rulesets, { verbose });
      linter.applyRules(rules.getRules());
  
      const spec = await loader.readOrError(
          specFile,
          buildLoaderOptions(jsonSchema, verbose)
      );
  
      return new Promise((resolve, reject) => {
          validator.validate(spec, buildValidatorOptions(skip, verbose), (err, _options) => {
              const { context, warnings, valid } = _options || err.options;
  
              if (err && valid === false) {
                  console.error(colors.red + 'Specification schema is invalid.' + colors.reset);
                  console.error(formatSchemaError(err, context));
                  return reject();
              }
  
              if (warnings.length) {
                  console.error(colors.red + 'Specification contains lint errors: ' + warnings.length + colors.reset);
                  console.warn(formatLintResults(warnings))
                  return reject();
              }
  
              if (!cmd.quiet) {
                  console.log(colors.green + 'Specification is valid, with 0 lint errors' + colors.reset)
              }
  
              return resolve();
          });
      });
  };
  
  const buildLoaderOptions = (jsonSchema, verbose) => {
      const options = {
          filters: [],
          resolve: true,
          verbose,
      };
  
      if (jsonSchema) {
          options.filters.push(fromJsonSchema);
      }
  
      return options;
  }
  
  const buildValidatorOptions = (skip, verbose) => {
      return {
          skip,
          lint: true,
          linter: linter.lint,
          linterResults: linter.getResults,
          prettify: true,
          verbose,
      };
  }
  
  module.exports = { command };
  