async function create(options = {}) {
  const {
    base,
    src,
    html,
    inline,
    ignore,
    extract,
    target = {},
    inlineImages,
    maxImageFileSize,
    postcss: postProcess = [],
    strict,
    cleanCSS: cleanCSSOptions,
    concurrency = Number.POSITIVE_INFINITY,
    assetPaths = [],
  } = options;

  // Create vinyl representation for the document with normalized filepath and normalized styles
  const document = src ? await getDocument(src, options) : await getDocumentFromSource(html, options);

  if (!document.css || !document.css.toString()) {
    if (strict) {
      throw new NoCssError();
    }

    return {
      css: '',
      html: document.contents.toString(),
    };
  }

  // Generate critical css
  let criticalCSS;
  try {
    const tasks = callPenthouse(document, options);
    const criticalStyles = await pAll(tasks, {concurrency});
    criticalCSS = combineCss(criticalStyles);
  } catch (error) {
    if (error.message === PAGE_UNLOADED_DURING_EXECUTION_ERROR_MESSAGE) {
      process.stderr.write(pico.yellow(PAGE_UNLOADED_DURING_EXECUTION_ERROR_MESSAGE) + EOL);
      return {
        css: '',
        html: document.contents.toString(),
      };
    }

    throw error;
  }

  // Add postprocess configuration
  if (ignore) {
    postProcess.push(discard(ignore));
  }

  if (inlineImages) {
    const refAssets = [...parseCssUrls(criticalCSS), ...document.stylesheets];
    const refAssetPaths = refAssets.reduce((res, file) => [...res, path.dirname(file)], []);

    const searchpaths = await reduceAsync([], [...new Set(refAssetPaths)], async (res, file) => {
      const paths = await getAssetPaths(document, file, options, false);
      return [...new Set([...res, ...paths])];
    });

    const filtered = searchpaths.filter((p) => isRemote(p) || p.includes(process.cwd()) || (base && p.includes(base)));

    const inlineOptions = {
      assetPaths: [...filtered, ...assetPaths],
      maxFileSize: maxImageFileSize,
    };

    debug('Inline images:', inlineOptions, refAssets);

    postProcess.push(imageInliner(inlineOptions));
  }

  // Post-process critical css
  if (postProcess.length > 0) {
    criticalCSS = await postcss(postProcess)
      .process(criticalCSS, {from: undefined})
      .then((contents) => contents.css);
  }

  // Minify or prettify
  const cleanCSS = new CleanCSS(
    cleanCSSOptions || {
      level: {
        1: {
          all: true,
        },
        2: {
          all: false,
          removeDuplicateFontRules: true,
          removeDuplicateMediaBlocks: true,
          removeDuplicateRules: true,
          removeEmpty: true,
          mergeMedia: true,
        },
      },
    }
  );
  criticalCSS = cleanCSS.minify(criticalCSS).styles;

  const result = {
    css: criticalCSS,
  };

  // Define uncritical as lazy evaluated property
  const lazyUncritical = (orig, diff) =>
    function () {
      this._uncritical ||= removeDuplicateStyles(orig, diff);

      return this._uncritical;
    };

  Object.defineProperty(result, 'uncritical', {
    get: lazyUncritical(document.css, criticalCSS),
  });

  // Inline
  if (inline) {
    const {replaceStylesheets} = inline;

    if (typeof replaceStylesheets === 'function') {
      inline.replaceStylesheets = await replaceStylesheets(document, result.uncritical);
    }

    // If replaceStylesheets is not set via option and and uncritical is empty
    if (extract && replaceStylesheets === undefined && result.uncritical.trim() === '') {
      inline.replaceStylesheets = [];
    }

    if (target.uncritical) {
      const uncriticalHref = normalizePath(path.relative(document.cwd, path.resolve(base, target.uncritical)));
      // Only replace stylesheets if the uncriticalHref is inside document.cwd and replaceStylesheets is not set via options
      if (!/^\.\.\//.test(uncriticalHref) && replaceStylesheets === undefined) {
        inline.replaceStylesheets = [`/${uncriticalHref}`];
      }
    } else {
      inline.extract = extract;
    }

    const inlined = inlineCritical(document.contents.toString(), criticalCSS, {...inline, basePath: document.cwd});
    document.contents = Buffer.from(inlined);
  }

  // Clean tempfiles
  await document.cleanup();

  result.html = document.contents.toString();

  // Cleanup output
  return result;
}
Critical path CSS generation
@param  {object} options Options
@accepts src, base, width, height, dimensions, dest
@return {Promise<object>} Object with critical css & html