Skip to content

Commit 4bd2aa8

Browse files
authored
Tagging 'implements-federated-plugin:<name>' for packages that implement a plugin. (#8933)
1 parent 1b1a6e7 commit 4bd2aa8

File tree

6 files changed

+84
-34
lines changed

6 files changed

+84
-34
lines changed

app/lib/frontend/templates/views/pkg/info_box.dart

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:_pub_shared/format/encoding.dart';
6+
import 'package:_pub_shared/search/tags.dart';
67
import 'package:pana/pana.dart';
78
import 'package:pub_dev/service/download_counts/download_counts.dart';
89
import 'package:pubspec_parse/pubspec_parse.dart' as pubspek;
@@ -105,7 +106,10 @@ d.Node packageInfoBoxNode({
105106
),
106107
if (license != null) _block('License', license),
107108
if (dependencies != null) _block('Dependencies', dependencies),
108-
_more(package.name!),
109+
_more(
110+
package.name!,
111+
showImplementsLink: data.version.pubspec?.hasFlutterPlugin ?? false,
112+
),
109113
]);
110114
}
111115

@@ -160,14 +164,26 @@ d.Node _metadata({
160164
]);
161165
}
162166

163-
d.Node _more(String packageName) {
167+
d.Node _more(String packageName, {required bool showImplementsLink}) {
164168
return _block(
165169
'More',
166-
d.a(
167-
href: urls.searchUrl(q: 'dependency:$packageName'),
168-
rel: 'nofollow',
169-
text: 'Packages that depend on $packageName',
170-
),
170+
d.fragment([
171+
d.a(
172+
href: urls.searchUrl(q: 'dependency:$packageName'),
173+
rel: 'nofollow',
174+
text: 'Packages that depend on $packageName',
175+
),
176+
if (showImplementsLink) ...[
177+
d.br(),
178+
d.br(),
179+
d.a(
180+
href: urls.searchUrl(
181+
q: PackageVersionTags.implementsFederatedPlugin(packageName)),
182+
rel: 'nofollow',
183+
text: 'Packages that implement $packageName',
184+
),
185+
],
186+
]),
171187
);
172188
}
173189

app/lib/package/model_properties.dart

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ Map<String, dynamic> _loadYaml(String yamlString) {
2929
class Pubspec {
3030
final pubspek.Pubspec _inner;
3131
final String jsonString;
32-
Map<String, dynamic>? _json;
3332
String? _canonicalVersion;
3433

3534
Pubspec._(this._inner, this.jsonString);
@@ -44,10 +43,9 @@ class Pubspec {
4443
factory Pubspec.fromJson(Map<String, dynamic> map) =>
4544
Pubspec._(pubspek.Pubspec.fromJson(map, lenient: true), json.encode(map));
4645

47-
Map<String, dynamic> get asJson {
48-
_load();
49-
return _json!;
50-
}
46+
late final _json = _loadYaml(jsonString);
47+
48+
Map<String, dynamic> get asJson => _json;
5149

5250
String get name => _inner.name;
5351

@@ -84,8 +82,7 @@ class Pubspec {
8482
.toList();
8583

8684
Map<String, dynamic>? get executables {
87-
_load();
88-
final map = _json!['executables'];
85+
final map = _json['executables'];
8986
return map is Map<String, dynamic> ? map : null;
9087
}
9188

@@ -97,7 +94,6 @@ class Pubspec {
9794
/// Returns null if the constraint is missing or does not follow the
9895
/// `>=<version>` pattern.
9996
MinSdkVersion? get minSdkVersion {
100-
_load();
10197
return MinSdkVersion.tryParse(_inner.environment['sdk']);
10298
}
10399

@@ -106,7 +102,6 @@ class Pubspec {
106102
/// Returns null if the constraint is missing or does not follow the
107103
/// `>=<version>` pattern.
108104
late final _minFlutterSdkVersion = () {
109-
_load();
110105
return MinSdkVersion.tryParse(_inner.environment['flutter']);
111106
}();
112107

@@ -162,27 +157,32 @@ class Pubspec {
162157
.intersect(VersionConstraint.parse('<2.12.0-0'))
163158
.isEmpty;
164159

165-
/// Whether the pubspec file contains a flutter.plugin entry.
166-
bool get hasFlutterPlugin {
167-
_load();
168-
final flutter = _json!['flutter'];
169-
if (flutter == null || flutter is! Map) return false;
160+
late final _flutterPluginMap = () {
161+
final flutter = _json['flutter'];
162+
if (flutter == null || flutter is! Map) {
163+
return null;
164+
}
170165
final plugin = flutter['plugin'];
171-
return plugin != null && plugin is Map;
172-
}
166+
if (plugin != null && plugin is Map<String, dynamic>) {
167+
return plugin;
168+
} else {
169+
return null;
170+
}
171+
}();
172+
173+
/// Whether the pubspec file contains a flutter.plugin entry.
174+
bool get hasFlutterPlugin => _flutterPluginMap != null;
173175

174176
/// Whether the package has a dependency on flutter.
175177
bool get dependsOnFlutter {
176-
_load();
177-
final dependencies = _json!['dependencies'];
178+
final dependencies = _json['dependencies'];
178179
if (dependencies == null || dependencies is! Map) return false;
179180
return dependencies.containsKey('flutter');
180181
}
181182

182183
/// Whether the package has a dependency on flutter and it refers to the SDK.
183184
bool get dependsOnFlutterSdk {
184-
_load();
185-
final dependencies = _json!['dependencies'];
185+
final dependencies = _json['dependencies'];
186186
if (dependencies == null || dependencies is! Map) return false;
187187
final flutter = dependencies['flutter'];
188188
if (flutter == null || flutter is! Map) return false;
@@ -195,14 +195,24 @@ class Pubspec {
195195
bool get hasOptedIntoNullSafety =>
196196
_sdkConstraintStatus.hasOptedIntoNullSafety;
197197

198-
void _load() {
199-
_json ??= _loadYaml(jsonString);
200-
}
201-
202198
late final List<Uri> funding = _inner.funding ?? const <Uri>[];
203199

204200
/// Whether the pubspec has any topic entry.
205201
bool get hasTopic => canonicalizedTopics.isNotEmpty;
202+
203+
/// If package is implementing a federated Flutter plugin, this will be name
204+
/// of the plugin package, `null` otherwise.
205+
late final implementsFederatedPluginName = () {
206+
if (_flutterPluginMap == null) {
207+
return null;
208+
}
209+
final implements = _flutterPluginMap['implements'];
210+
if (implements != null && implements is String) {
211+
return implements;
212+
} else {
213+
return null;
214+
}
215+
}();
206216
}
207217

208218
class MinSdkVersion {

app/lib/package/models.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,13 +658,18 @@ class PackageVersion extends db.ExpandoModel<String> {
658658

659659
/// List of tags from the properties on the current [PackageVersion] entity.
660660
Iterable<String> getTags() {
661+
final pluginForName = pubspec!.implementsFederatedPluginName;
661662
return <String>{
662663
if (pubspec!.supportsOnlyLegacySdk) ...[
663664
PackageVersionTags.isLegacy,
664665
PackageTags.isUnlisted,
665666
],
666667
if (pubspec!.funding.isNotEmpty) PackageVersionTags.hasFundingLink,
667668
if (pubspec!.hasTopic) PackageVersionTags.hasTopic,
669+
if (pluginForName != null) ...[
670+
PackageVersionTags.hasImplementsFederatedPlugin,
671+
PackageVersionTags.implementsFederatedPlugin(pluginForName),
672+
],
668673
};
669674
}
670675

pkg/_pub_shared/lib/search/search_form.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ final RegExp _allDependencyRegExp =
1717
final _sortRegExp = RegExp('sort:([a-z]+)');
1818
final _updatedRegExp = RegExp('updated:([0-9][0-9a-z]*)');
1919
final _tagRegExp =
20-
RegExp(r'([\+|\-]?[a-z0-9]+:[a-z0-9\-_\.]+)', caseSensitive: false);
20+
RegExp(r'([\+|\-]?[a-z0-9\-]+:[a-z0-9\-_\.]+)', caseSensitive: false);
2121

2222
/// The tag prefixes that we can detect in the user-provided search query.
2323
final _detectedTagPrefixes = <String>{

pkg/_pub_shared/lib/search/tags.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ const allowedTagPrefixes = [
1616
'sdk:',
1717
'show:',
1818
'has:',
19-
'topic:'
19+
'topic:',
20+
'implements-federated-plugin:',
2021
];
2122

2223
/// Collection of package-related tags.
@@ -87,6 +88,14 @@ abstract class PackageVersionTags {
8788
/// Package version may be used in WASM compilation.
8889
static const String isWasmReady = 'is:wasm-ready';
8990

91+
/// Package version has an entry indicating it implements a federated plugin.
92+
static const String hasImplementsFederatedPlugin =
93+
'has:implements-federated-plugin';
94+
95+
/// The `implements-federated-plugin:<name>` tag.
96+
static String implementsFederatedPlugin(String name) =>
97+
'implements-federated-plugin:$name';
98+
9099
/// Version tags that provide a positive, forward-looking property
91100
/// of a prerelease or preview version.
92101
///
@@ -167,5 +176,7 @@ const _futureVersionTags = <String>{
167176
/// Returns whether a [tag] is relevant to the package search,
168177
/// if it is a value from a preview or prerelease version.
169178
bool isFutureVersionTag(String tag) {
170-
return _futureVersionTags.contains(tag) || tag.startsWith('runtime:');
179+
return _futureVersionTags.contains(tag) ||
180+
tag.startsWith('runtime:') ||
181+
tag.startsWith('plugin-for:');
171182
}

pkg/_pub_shared/test/search/search_form_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ void main() {
180180
query.parsedQuery.tagsPredicate.toQueryParameters(), ['is:legacy']);
181181
});
182182

183+
test('complex tag', () {
184+
final query =
185+
SearchForm(query: 'implements-federated-plugin:url_launcher');
186+
expect(query.parsedQuery.text, isNull);
187+
expect(query.parsedQuery.tagsPredicate.toQueryParameters(),
188+
['implements-federated-plugin:url_launcher']);
189+
});
190+
183191
test('forbidden known tag', () {
184192
final query = SearchForm(query: '-is:legacy');
185193
expect(query.parsedQuery.text, isNull);

0 commit comments

Comments
 (0)