blob: aac7aa7b753dc3b683e14ff293209080b7b19f2e [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/build.dart';
import 'package:path/path.dart' as p;
/// Returns a non-null name for the provided [type].
///
/// In newer versions of the Dart analyzer, a `typedef` does not keep the
/// existing `name`, because it is used an alias:
/// ```
/// // Used to return `VoidFunc` for name, is now `null`.
/// typedef VoidFunc = void Function();
/// ```
///
/// This function will return `'VoidFunc'`, unlike [DartType.name].
String typeNameOf(DartType type) {
if (type is FunctionType) {
final element = type.element;
if (element is GenericFunctionTypeElement) {
return element.enclosingElement.name;
}
}
return type.name;
}
/// Returns a name suitable for `part of "..."` when pointing to [element].
///
/// Returns `null` if [element] is missing identifier.
String nameOfPartial(LibraryElement element, AssetId source) {
if (element.name != null && element.name.isNotEmpty) {
return element.name;
}
final sourceUrl = p.basename(source.uri.toString());
return '\'$sourceUrl\'';
}
/// Returns a suggested library identifier based on [source] path and package.
String suggestLibraryName(AssetId source) {
// lib/test.dart --> [lib/test.dart]
final parts = source.path.split('/');
// [lib/test.dart] --> [lib/test]
parts[parts.length - 1] = parts.last.split('.').first;
// [lib/test] --> [test]
if (parts.first == 'lib') {
parts.removeAt(0);
}
return '${source.package}.${parts.join('.')}';
}
/// Returns what 'part "..."' URL is needed to import [output] from [input].
///
/// For example, will return `test_lib.g.dart` for `test_lib.dart`.
String computePartUrl(AssetId input, AssetId output) =>
p.joinAll(p.split(p.relative(output.path, from: input.path)).skip(1));
/// Returns a URL representing [element].
String urlOfElement(Element element) => element.kind == ElementKind.DYNAMIC
? 'dart:core#dynmaic'
// using librarySource.uri – in case the element is in a part
: normalizeUrl(element.librarySource.uri)
.replace(fragment: element.name)
.toString();
Uri normalizeUrl(Uri url) {
switch (url.scheme) {
case 'dart':
return normalizeDartUrl(url);
case 'package':
return packageToAssetUrl(url);
default:
return url;
}
}
/// Make `dart:`-type URLs look like a user-knowable path.
///
/// Some internal dart: URLs are something like `dart:core/map.dart`.
///
/// This isn't a user-knowable path, so we strip out extra path segments
/// and only expose `dart:core`.
Uri normalizeDartUrl(Uri url) => url.pathSegments.isNotEmpty
? url.replace(pathSegments: url.pathSegments.take(1))
: url;
/// Returns a `package:` URL converted to a `asset:` URL.
///
/// This makes internal comparison logic much easier, but still allows users
/// to define assets in terms of `package:`, which is something that makes more
/// sense to most.
///
/// For example, this transforms `package:source_gen/source_gen.dart` into:
/// `asset:source_gen/lib/source_gen.dart`.
Uri packageToAssetUrl(Uri url) => url.scheme == 'package'
? url.replace(
scheme: 'asset',
pathSegments: <String>[]
..add(url.pathSegments.first)
..add('lib')
..addAll(url.pathSegments.skip(1)))
: url;
/// Returns a `asset:` URL converted to a `package:` URL.
///
/// For example, this transformers `asset:source_gen/lib/source_gen.dart' into:
/// `package:source_gen/source_gen.dart`. Asset URLs that aren't pointing to a
/// file in the 'lib' folder are not modified.
///
/// Asset URLs come from `package:build`, as they are able to describe URLs that
/// are not describable using `package:...`, such as files in the `bin`, `tool`,
/// `web`, or even root directory of a package - `asset:some_lib/web/main.dart`.
Uri assetToPackageUrl(Uri url) => url.scheme == 'asset' &&
url.pathSegments.isNotEmpty &&
url.pathSegments[1] == 'lib'
? url.replace(
scheme: 'package',
pathSegments: [url.pathSegments.first]
..addAll(url.pathSegments.skip(2)))
: url;