Writes module scripts to output HTML file and copy necessary polyfills.
function writeModules(pluginConfig, outputConfig, entryModules) {
  const indexHTML = getOutputHTML(outputConfig.dir);
  const head = query(indexHTML, predicates.hasTagName('head'));
  const body = query(indexHTML, predicates.hasTagName('body'));

  // If dynamic imports are polyfilled, we first need to feature detect so we can't load our app using the
  // 'src' attribute.
  if (pluginConfig.polyfillDynamicImports) {
    // Feature detect dynamic import in a separate script, because browsers which don't
    // support it will throw a syntax error and stop executing the module
    append(body, createScriptModule('window.importModule=src=>import(src);'));

    // The actual loading script. Loads web components and dynamic import polyfills if necessary, then
    // loads the user's modules
    append(
      body,
      createScriptModule(`
      (async () => {
        const p = [];
        if (!('attachShadow' in Element.prototype) || !('getRootNode' in Element.prototype)) {p.push('./polyfills/webcomponent-polyfills.js');}
        if (!window.importModule) {p.push('./polyfills/dynamic-import-polyfill.js');}
        if (p.length > 0) {await Promise.all(p.map(p => new Promise((rs, rj) => {const s = document.createElement('script');s.src = p;s.onerror = () => rj(\`Failed to load \${s.s}\`);s.onload = () => rs();document.head.appendChild(s);})));}
        ${entryModules.map(src => `importModule('${src}');`).join('')}
      })();
    `),
    );

    // Because the module is loaded after some js execution, add preload hints so that it is downloaded
    // from the start
    entryModules.forEach(src => {
      append(
        head,
        createElement('link', {
          rel: 'preload',
          as: 'script',
          crossorigin: 'anonymous',
          href: src,
        }),
      );
    });
  } else {
    // No polyfilling needed, so just create a module script
    entryModules.forEach(src => {
      append(body, createScript({ type: 'module', src }));
    });
  }

  writeOutputHTML(outputConfig.dir, indexHTML);
}