index.js
async function addIncludesToHtml(html, overrideMap,
    opt_options) {
  let instance = await amphtmlValidator.getInstance();
  const options = opt_options || {};
  if (options.updateComponentsMap) {
    await updateComponentMap();
  }
  const versionMap = await readComponentsMap(COMPONENTS_MAP_PATH);

  const result = instance.validateString(html);

  const placeholder = options.placeholder || DEFAULT_AMP_PLACEHOLDER;
  const mode = options.mode || DEFAULT_INSERTION_MODE;

  // It is necessary to escape the placeholder as it is used in a RegExp object
  // for example ${ampjs} -> \\$\\{ampjs\\}.
  // Furthermore, the regular expression is defined to also match any
  // preceding whitespace too, so that inserted tags can be indented to match.
  const escapedPlaceholder = new RegExp('([^\\S\\r\\n]*)'
      + escapeRegex(placeholder));

  var missingScriptUrls = new Set();
  if (result.status === 'FAIL') {
    // Determine whether the base AMP script element is missing.
    for (err of result.errors) {
      if (err.category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT'
          && err.code === 'MANDATORY_TAG_MISSING'
          && err.params && err.params[0] === 'amphtml engine v0.js script') {
        missingScriptUrls.add(AMP_BASE_URL_ELEMENT);
        break;
      }
    }
    // Filter for only those errors indicating a missing script tag.
    const tagErrors = result.errors
        .filter(err => {
            return err.category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT'
                && (err.code === 'MISSING_REQUIRED_EXTENSION' 
                || err.code === 'ATTR_MISSING_REQUIRED_EXTENSION')});
    for (let tagError of tagErrors) {
      const tagName = tagError.params[1];
      if (overrideMap[tagName]) {
        var tagVersion = overrideMap[tagName];
      } else if (options.forceLatest) {
        var tagVersion = 'latest';
      } else if (versionMap[tagName]) {
        var tagVersion = versionMap[tagName];
      }

      if (!tagVersion) {
        throw Error('Unknown AMP Component ' + tagName);
      }
      missingScriptUrls.add(createAmpCustomElementTag(tagName, tagVersion));
    }
  }
  if (missingScriptUrls.size) {
    if (mode === MODES.PLACEHOLDER) {
      return addScriptUrlsByPlaceHolder(html, missingScriptUrls,
          escapedPlaceholder);
    } else {
      return addScriptUrlsByHeaderInsertion(html, missingScriptUrls);
    }
  }
  return html;
}
async function addIncludesToHtml(html, overrideMap,
    opt_options) {
  let instance = await amphtmlValidator.getInstance();
  const options = opt_options || {};
  if (options.updateComponentsMap) {
    await updateComponentMap();
  }
  const versionMap = await readComponentsMap(COMPONENTS_MAP_PATH);

  const result = instance.validateString(html);

  const placeholder = options.placeholder || DEFAULT_AMP_PLACEHOLDER;
  const mode = options.mode || DEFAULT_INSERTION_MODE;

  // It is necessary to escape the placeholder as it is used in a RegExp object
  // for example ${ampjs} -> \\$\\{ampjs\\}.
  // Furthermore, the regular expression is defined to also match any
  // preceding whitespace too, so that inserted tags can be indented to match.
  const escapedPlaceholder = new RegExp('([^\\S\\r\\n]*)'
      + escapeRegex(placeholder));

  var missingScriptUrls = new Set();
  if (result.status === 'FAIL') {
    // Determine whether the base AMP script element is missing.
    for (err of result.errors) {
      if (err.category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT'
          && err.code === 'MANDATORY_TAG_MISSING'
          && err.params && err.params[0] === 'amphtml engine v0.js script') {
        missingScriptUrls.add(AMP_BASE_URL_ELEMENT);
        break;
      }
    }
    // Filter for only those errors indicating a missing script tag.
    const tagErrors = result.errors
        .filter(err => {
            return err.category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT'
                && (err.code === 'MISSING_REQUIRED_EXTENSION' 
                || err.code === 'ATTR_MISSING_REQUIRED_EXTENSION')});
    for (let tagError of tagErrors) {
      const tagName = tagError.params[1];
      if (overrideMap[tagName]) {
        var tagVersion = overrideMap[tagName];
      } else if (options.forceLatest) {
        var tagVersion = 'latest';
      } else if (versionMap[tagName]) {
        var tagVersion = versionMap[tagName];
      }

      if (!tagVersion) {
        throw Error('Unknown AMP Component ' + tagName);
      }
      missingScriptUrls.add(createAmpCustomElementTag(tagName, tagVersion));
    }
  }
  if (missingScriptUrls.size) {
    if (mode === MODES.PLACEHOLDER) {
      return addScriptUrlsByPlaceHolder(html, missingScriptUrls,
          escapedPlaceholder);
    } else {
      return addScriptUrlsByHeaderInsertion(html, missingScriptUrls);
    }
  }
  return html;
}
async function addIncludesToHtml ( html , overrideMap , opt_options ) { let instance = await amphtmlValidator . getInstance ( ) ; const options = opt_options || { } ; if ( options . updateComponentsMap ) { await updateComponentMap ( ) ; } const versionMap = await readComponentsMap ( COMPONENTS_MAP_PATH ) ; const result = instance . validateString ( html ) ; const placeholder = options . placeholder || DEFAULT_AMP_PLACEHOLDER ; const mode = options . mode || DEFAULT_INSERTION_MODE ; // It is necessary to escape the placeholder as it is used in a RegExp object // for example ${ampjs} -> \\$\\{ampjs\\}. // Furthermore, the regular expression is defined to also match any // preceding whitespace too, so that inserted tags can be indented to match. const escapedPlaceholder = new RegExp ( '([^\\S\\r\\n]*)' + escapeRegex ( placeholder ) ) ; var missingScriptUrls = new Set ( ) ; if ( result . status === 'FAIL' ) { // Determine whether the base AMP script element is missing. for ( err of result . errors ) { if ( err . category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT' && err . code === 'MANDATORY_TAG_MISSING' && err . params && err . params [ 0 ] === 'amphtml engine v0.js script' ) { missingScriptUrls . add ( AMP_BASE_URL_ELEMENT ) ; break ; } } // Filter for only those errors indicating a missing script tag. const tagErrors = result . errors . filter ( err => { return err . category === 'MANDATORY_AMP_TAG_MISSING_OR_INCORRECT' && ( err . code === 'MISSING_REQUIRED_EXTENSION' || err . code === 'ATTR_MISSING_REQUIRED_EXTENSION' ) } ) ; for ( let tagError of tagErrors ) { const tagName = tagError . params [ 1 ] ; if ( overrideMap [ tagName ] ) { var tagVersion = overrideMap [ tagName ] ; } else if ( options . forceLatest ) { var tagVersion = 'latest' ; } else if ( versionMap [ tagName ] ) { var tagVersion = versionMap [ tagName ] ; } if ( ! tagVersion ) { throw Error ( 'Unknown AMP Component ' + tagName ) ; } missingScriptUrls . add ( createAmpCustomElementTag ( tagName , tagVersion ) ) ; } } if ( missingScriptUrls . size ) { if ( mode === MODES . PLACEHOLDER ) { return addScriptUrlsByPlaceHolder ( html , missingScriptUrls , escapedPlaceholder ) ; } else { return addScriptUrlsByHeaderInsertion ( html , missingScriptUrls ) ; } } return html ; }
Identifies missing AMP custom-element script tags in an AMP HTML test and
adds them.

This is achieved by:
1. Running the AMP validator and filtering for errors for missing extensions.
2. Creating <script> elements using versions taken from GitHub directory
listings.

@param {!Vinyl} file The file to add script tags to.
@param {Object} overrideMap Lookup of component versions for this project.
@param {Object} opt_options See {@code create}.
@return {!Vinyl} The modified file.
Identifies missing AMP custom - element script tags in an AMP HTML test and adds them .