canonicalize method

  1. @override
Uri? canonicalize(
  1. Uri url
)
override

If url is recognized by this importer, returns its canonical format.

Note that canonical URLs must be absolute, including a scheme. Returning file: URLs is encouraged if the imported stylesheet comes from a file on disk.

If Sass has already loaded a stylesheet with the returned canonical URL, it re-uses the existing parse tree. This means that importers must ensure that the same canonical URL always refers to the same stylesheet, even across different importers.

This may return null if url isn't recognized by this importer.

If this importer's URL format supports file extensions, it should canonicalize them the same way as the default filesystem importer:

  • The importer should look for stylesheets by adding the prefix _ to the URL's basename, and by adding the extensions .sass and .scss if the URL doesn't already have one of those extensions. For example, if the URL was foo/bar/baz, the importer would look for:

    • foo/bar/baz.sass
    • foo/bar/baz.scss
    • foo/bar/_baz.sass
    • foo/bar/_baz.scss

    If the URL was foo/bar/baz.scss, the importer would just look for:

    • foo/bar/baz.scss
    • foo/bar/_baz.scss

    If the importer finds a stylesheet at more than one of these URLs, it should throw an exception indicating that the import is ambiguous. Note that if the extension is explicitly specified, a stylesheet with the opposite extension may exist.

  • If none of the possible paths is valid, the importer should perform the same resolution on the URL followed by /index. In the example above, it would look for:

    • foo/bar/baz/_index.sass
    • foo/bar/baz/index.sass
    • foo/bar/baz/_index.scss
    • foo/bar/baz/index.scss

    As above, if the importer finds a stylesheet at more than one of these URLs, it should throw an exception indicating that the import is ambiguous.

If no stylesheets are found, the importer should return null.

Calling canonicalize multiple times with the same URL must return the same result. Calling canonicalize with a URL returned by canonicalize must return that URL. Calling canonicalize with a URL relative to one returned by canonicalize must return a meaningful result.

Implementation

@override
Uri? canonicalize(Uri url) {
  if (url.scheme == 'file') return FilesystemImporter.cwd.canonicalize(url);
  if (url.scheme != 'pkg') return null;

  if (url.hasAuthority) {
    throw "A pkg: URL must not have a host, port, username or password.";
  } else if (p.url.isAbsolute(url.path)) {
    throw "A pkg: URL's path must not begin with /.";
  } else if (url.path.isEmpty) {
    throw "A pkg: URL must not have an empty path.";
  } else if (url.hasQuery || url.hasFragment) {
    throw "A pkg: URL must not have a query or fragment.";
  }

  var baseDirectory = containingUrl?.scheme == 'file'
      ? p.dirname(p.fromUri(containingUrl!))
      : _entryPointDirectory;

  var (packageName, subpath) = _packageNameAndSubpath(url.path);

  // If the package name is not a valid Node package name, return null in case
  // another importer can handle.
  if (packageName.startsWith('.') ||
      packageName.contains('\\') ||
      packageName.contains('%') ||
      (packageName.startsWith('@') &&
          !packageName.contains(p.url.separator))) {
    return null;
  }

  var packageRoot = _resolvePackageRoot(packageName, baseDirectory);

  if (packageRoot == null) return null;
  var jsonPath = p.join(packageRoot, 'package.json');

  var jsonString = readFile(jsonPath);
  Map<String, dynamic> packageManifest;
  try {
    packageManifest = json.decode(jsonString) as Map<String, dynamic>;
  } catch (e) {
    throw "Failed to parse $jsonPath for \"pkg:$packageName\": $e";
  }

  if (_resolvePackageExports(
          packageRoot, subpath, packageManifest, packageName)
      case var resolved?) {
    if (_validExtensions.contains(p.extension(resolved))) {
      return p.toUri(p.canonicalize(resolved));
    } else {
      throw "The export for '${subpath ?? "root"}' in "
          "'$packageName' resolved to '${resolved.toString()}', "
          "which is not a '.scss', '.sass', or '.css' file.";
    }
  }
  // If no subpath, attempt to resolve `sass` or `style` key in package.json,
  // then `index` file at package root, resolved for file extensions and
  // partials.
  if (subpath == null) {
    var rootPath = _resolvePackageRootValues(packageRoot, packageManifest);
    return rootPath != null ? p.toUri(p.canonicalize(rootPath)) : null;
  }

  // If there is a subpath, attempt to resolve the path relative to the
  // package root, and resolve for file extensions and partials.
  var subpathInRoot = p.join(packageRoot, subpath);
  return FilesystemImporter.cwd.canonicalize(p.toUri(subpathInRoot));
}