canonicalize method

CanonicalizeResult? canonicalize(
  1. Uri url, {
  2. Importer? baseImporter,
  3. Uri? baseUrl,
  4. bool forImport = false,
})

Canonicalizes url according to one of this cache's importers.

The baseUrl should be the canonical URL of the stylesheet that contains the load, if it exists.

Returns the importer that was used to canonicalize url, the canonical URL, and the URL that was passed to the importer (which may be resolved relative to baseUrl if it's passed).

If baseImporter is non-null, this first tries to use baseImporter to canonicalize url (resolved relative to baseUrl if it's passed).

If any importers understand url, returns that importer as well as the canonicalized URL and the original URL (resolved relative to baseUrl if applicable). Otherwise, returns null.

Implementation

CanonicalizeResult? canonicalize(Uri url,
    {Importer? baseImporter, Uri? baseUrl, bool forImport = false}) {
  if (isBrowser &&
      (baseImporter == null || baseImporter is NoOpImporter) &&
      _importers.isEmpty) {
    throw "Custom importers are required to load stylesheets when compiling "
        "in the browser.";
  }

  if (baseImporter != null && url.scheme == '') {
    var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
    var key = (baseImporter, resolvedUrl, forImport: forImport);
    var relativeResult = _perImporterCanonicalizeCache.putIfAbsent(key, () {
      var (result, cacheable) =
          _canonicalize(baseImporter, resolvedUrl, baseUrl, forImport);
      assert(
          cacheable,
          "Relative loads should always be cacheable because they never "
          "provide access to the containing URL.");
      if (baseUrl != null) _nonCanonicalRelativeUrls[key] = url;
      return result;
    });
    if (relativeResult != null) return relativeResult;
  }

  var key = (url, forImport: forImport);
  if (_canonicalizeCache.containsKey(key)) return _canonicalizeCache[key];

  // Each indivudal call to a `canonicalize()` override may not be cacheable
  // (specifically, if it has access to `containingUrl` it's too
  // context-sensitive to usefully cache). We want to cache a given URL across
  // the _entire_ importer chain, so we use [cacheable] to track whether _all_
  // `canonicalize()` calls we've attempted are cacheable. Only if they are do
  // we store the result in the cache.
  var cacheable = true;
  for (var i = 0; i < _importers.length; i++) {
    var importer = _importers[i];
    var perImporterKey = (importer, url, forImport: forImport);
    switch (_perImporterCanonicalizeCache.getOption(perImporterKey)) {
      case (var result?,):
        return result;
      case (null,):
        continue;
    }

    switch (_canonicalize(importer, url, baseUrl, forImport)) {
      case (var result?, true) when cacheable:
        _canonicalizeCache[key] = result;
        return result;

      case (var result, true) when !cacheable:
        _perImporterCanonicalizeCache[perImporterKey] = result;
        if (result != null) return result;

      case (var result, false):
        if (cacheable) {
          // If this is the first uncacheable result, add all previous results
          // to the per-importer cache so we don't have to re-run them for
          // future uses of this importer.
          for (var j = 0; j < i; j++) {
            _perImporterCanonicalizeCache[(
              _importers[j],
              url,
              forImport: forImport
            )] = null;
          }
          cacheable = false;
        }

        if (result != null) return result;
    }
  }

  if (cacheable) _canonicalizeCache[key] = null;
  return null;
}