canonicalize method
Future<AsyncCanonicalizeResult?>
canonicalize(
- Uri url, {
- AsyncImporter? baseImporter,
- Uri? baseUrl,
- 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
Future<AsyncCanonicalizeResult?> canonicalize(Uri url,
{AsyncImporter? baseImporter,
Uri? baseUrl,
bool forImport = false}) async {
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 =
await putIfAbsentAsync(_perImporterCanonicalizeCache, key, () async {
var (result, cacheable) =
await _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 (await _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;
}