function createIndexHTML(baseIndex, config) {
  const localConfig = deepmerge(defaultConfig, config);
  if (!baseIndex) {
    throw new Error('Missing baseIndex.');
  }

  if (!localConfig.entries || !localConfig.entries.files.length) {
    throw new Error('Invalid config: missing config.entries');
  }

  const head = query(baseIndex, predicates.hasTagName('head'));
  const body = query(baseIndex, predicates.hasTagName('body'));

  if (!head) {
    throw new Error(`Invalid index.html: missing <head>`);
  }

  if (!body) {
    throw new Error(`Invalid index.html: missing <body>`);
  }

  const firstScript = query(body, predicates.hasTagName('script'));

  /** @type {FileResult[]} */
  const files = [];
  const polyfills = getPolyfills(localConfig);

  /**
   * Check whether we need add a special loader script, or if we can load app
   * code directly with a script tag. A loader is needed when:
   * - We need to load polyfills conditonally
   * - We are loading system, which can't be loaded with a script tag
   * - We have a legacy build, so we need to conditionally load either modern or legacy
   */
  const needsLoader =
    polyfills.some(p => Boolean(p.test)) ||
    [localConfig.entries, localConfig.legacyEntries].some(c => c && c.type === 'system') ||
    (localConfig.legacyEntries && localConfig.legacyEntries.files.length > 0);

  const scripts = createScripts(localConfig.polyfills, polyfills, localConfig.entries, needsLoader);
  if (firstScript) {
    scripts.forEach(script => {
      insertBefore(body, firstScript, script);
    });
  } else {
    scripts.forEach(script => {
      append(body, script);
    });
  }

  const appendPreloadScript = href => {
    if (localConfig.entries.type === 'module') {
      append(head, createElement('link', { rel: 'modulepreload', href, as: 'script' }));
    } else {
      append(head, createElement('link', { rel: 'preload', href, as: 'script' }));
    }
  };

  let loaderCode;
  if (needsLoader) {
    loaderCode = createLoaderScript(
      localConfig.entries,
      localConfig.legacyEntries,
      polyfills,
      localConfig.polyfills,
      localConfig.loader === 'external',
    );

    if (localConfig.preload) {
      localConfig.entries.files.forEach(appendPreloadScript);
    }

    if (localConfig.loader === 'inline') {
      append(body, createScript(null, loaderCode));
    } else if (localConfig.loader === 'external') {
      const name = `loader.${createContentHash(loaderCode)}.js`;
      files.push({ path: name, content: loaderCode });
      append(body, createScript({ src: name }));
    } else {
      throw new Error(`Unknown loader mode: ${localConfig.loader}`);
    }
  }

  if (localConfig.entries.preloadedFiles) {
    localConfig.entries.preloadedFiles.forEach(appendPreloadScript);
  }

  const serialized = serialize(baseIndex);
  const result = localConfig.minify ? minifyIndexHTML(serialized, localConfig.minify) : serialized;

  polyfills.forEach(polyfill => {
    files.push({
      path: path.join('polyfills', `${polyfillFilename(polyfill, localConfig.polyfills)}.js`),
      content: polyfill.code,
    });
    files.push({
      path: path.join('polyfills', `${polyfillFilename(polyfill, localConfig.polyfills)}.js.map`),
      content: polyfill.sourcemap,
    });
  });

  return {
    indexHTML: result,
    files,
  };
}
Generates a index HTML based on the given configuration. A clean index.html should be

@param {ASTNode} baseIndex the base index.html
@param {Partial<CreateIndexHTMLConfig>} config
@returns {{ indexHTML: string, files: FileResult[] }} the updated index html