addAsset(assetConfig, incomingRelation) {
    if (
      Array.isArray(assetConfig) ||
      (typeof assetConfig === 'string' &&
        !/^[a-zA-Z-+]+:/.test(assetConfig) &&
        assetConfig.includes('*'))
    ) {
      throw new Error(
        'AssetGraph#addAsset does not accept an array or glob patterns, try the addAssets method or the loadAssets transform'
      );
    }
    const baseUrl = (incomingRelation && incomingRelation.baseUrl) || this.root;
    if (typeof assetConfig === 'string') {
      if (/^[a-zA-Z-+]+:/.test(assetConfig)) {
        if (!/^data:/i.test(assetConfig)) {
          assetConfig = normalizeUrl(assetConfig);
        }
        assetConfig = { url: assetConfig };
      } else {
        assetConfig = {
          url: this.resolveUrl(baseUrl, normalizeUrl(encodeURI(assetConfig))),
        };
      }
    }
    let asset;
    if (assetConfig.isAsset) {
      // An already instantiated asset
      asset = assetConfig;
      asset.assetGraph = this;
    } else {
      if (typeof assetConfig.url === 'string') {
        if (!/^[a-zA-Z-+]+:/.test(assetConfig.url)) {
          assetConfig.url = this.resolveUrl(baseUrl, assetConfig.url);
        }
        assetConfig.url = assetConfig.url.replace(/#.*$/, '');

        if (!/^data:/i.test(assetConfig.url)) {
          // Browsers interpret control chars and non-ASCII chars in urls as to percent-encoded utf-8 octets
          // Make the same transformation early so we treat these urls as identical
          assetConfig.url = assetConfig.url.replace(
            /[^\x21-\x7f]/g,
            encodeURIComponent
          );
        }

        if (this.canonicalRoot) {
          if (assetConfig.url.startsWith(this.canonicalRoot)) {
            assetConfig.url = assetConfig.url.replace(
              this.canonicalRoot,
              this.root
            );
          }

          // Canonical urls always end in slash, but should also match urls with no path or trailing slash.
          // Make a direct equality comparison without the slash
          if (assetConfig.url === this.canonicalRoot.slice(0, -1)) {
            assetConfig.url = this.root;
          }
        }
        if (/^data:/.test(assetConfig.url)) {
          const parsedDataUrl = resolveDataUrl(assetConfig.url);
          if (parsedDataUrl) {
            Object.assign(assetConfig, parsedDataUrl);
            assetConfig.url = undefined;
            assetConfig.type =
              assetConfig.type ||
              this.lookupContentType(assetConfig.contentType);
          } else {
            this.warn(
              new errors.ParseError(`Cannot parse data url: ${assetConfig.url}`)
            );
          }
        } else if (/^javascript:/i.test(assetConfig.url)) {
          assetConfig.text = decodeURIComponent(
            assetConfig.url.replace(/^javascript:/i, '')
          );
          assetConfig.url = undefined;
          assetConfig.type = 'JavaScript';
        } else {
          // Check if an asset with that url already exists in the graph,
          // and if it does, update it with the information contained
          // in assetConfig:
          asset = this._urlIndex[assetConfig.url];
          if (asset) {
            // New information about an existing asset has arrived
            asset.init(assetConfig);
          }
        }
      }
      if (!asset) {
        if (typeof assetConfig.url === 'undefined' && incomingRelation) {
          assetConfig.incomingInlineRelation = incomingRelation;
        }
        if (assetConfig instanceof this.Asset) {
          asset = assetConfig;
        } else {
          if (assetConfig.type) {
            asset = new AssetGraph[assetConfig.type](assetConfig, this);
          } else {
            asset = new this.Asset(assetConfig, this);
          }
          if (!incomingRelation && !asset.url) {
            // Non-inline asset without an url -- make up a unique url:
            asset.externalize();
          }
        }
      }
    }
    asset._tryUpgrade(undefined, incomingRelation);
    if (!this.idIndex[asset.id]) {
      this.idIndex[asset.id] = asset;
      if (asset.url) {
        this._urlIndex[asset.url] = asset;
      }
      this._assets.add(asset);
      asset.isPopulated = false;
      this.emit('addAsset', asset);
      asset.populate();
    }
    return asset;
  }
Add an asset to the graph.

@param {Asset|String|AssetConfig} assetConfig The Asset, Url or AssetConfig to construct an Asset from
@return {Asset} The assets instance that was added