Skip to content

Commit 3a3740c

Browse files
authored
Surface lint release version on lint pages (#7187)
1 parent de6c0da commit 3a3740c

File tree

5 files changed

+70
-11
lines changed

5 files changed

+70
-11
lines changed

site/lib/_sass/components/_tags.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@
6464
&.red {
6565
background-color: rgb(255, 205, 200);
6666
}
67+
68+
&.grey {
69+
background-color: rgb(230, 230, 230);
70+
}
6771
}
6872

6973
span.tag-label {

site/lib/src/components/common/tags.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:jaspr/dom.dart';
66
import 'package:jaspr/jaspr.dart';
77

8+
import '../../util.dart';
89
import 'material_icon.dart';
910

1011
/// A display of multiple categorical or descriptive tags.
@@ -28,16 +29,16 @@ class Tag extends StatelessComponent {
2829
this.icon,
2930
this.title,
3031
this.label,
31-
this.color,
3232
this.link,
33+
this.color = TagColor.blue,
3334
});
3435

3536
final String content;
3637
final String? icon;
3738
final String? title;
3839
final String? label;
39-
final String? color;
4040
final String? link;
41+
final TagColor color;
4142

4243
@override
4344
Component build(BuildContext context) {
@@ -49,20 +50,30 @@ class Tag extends StatelessComponent {
4950
'title': ?title,
5051
'aria-label': ?(label ?? title),
5152
};
53+
final classes = ['tag-label', color.name].toClasses;
5254

5355
if (link case final link?) {
5456
return a(
5557
href: link,
56-
classes: 'tag-label',
58+
classes: classes,
5759
attributes: attributes,
5860
children,
5961
);
6062
}
6163

6264
return div(
63-
classes: 'tag-label',
65+
classes: classes,
6466
attributes: attributes,
6567
children,
6668
);
6769
}
6870
}
71+
72+
/// A supported background color for a [Tag].
73+
enum TagColor {
74+
blue,
75+
green,
76+
orange,
77+
red,
78+
grey,
79+
}

site/lib/src/models/lints.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:convert';
66
import 'dart:io';
77

88
import 'package:path/path.dart' as p;
9+
import 'package:pub_semver/pub_semver.dart' show Version;
910

1011
import '../util.dart';
1112

@@ -48,4 +49,15 @@ extension type LintDetails(Map<String, Object?> details) {
4849
String get fixStatus => details['fixStatus'] as String;
4950
String get docs => details['details'] as String;
5051
String get sinceDartSdk => details['sinceDartSdk'] as String;
52+
Version? get releasedInVersion {
53+
try {
54+
final rawVersion = sinceDartSdk.split('.').length == 2
55+
? '$sinceDartSdk.0'
56+
: sinceDartSdk;
57+
return Version.parse(rawVersion);
58+
} on FormatException {
59+
// Assume the lint isn't released if the format isn't understood.
60+
}
61+
return null;
62+
}
5163
}

site/lib/src/pages/custom_pages.dart

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import '../components/common/tags.dart';
1414
import '../markdown/markdown_parser.dart';
1515
import '../models/diagnostic_model.dart';
1616
import '../models/lints.dart';
17+
import '../utils/server_only_utils.dart';
1718
import 'glossary.dart';
1819

1920
/// All pages that should be loaded from memory rather than
@@ -184,43 +185,49 @@ List<MemoryPage> get _lintMemoryPages {
184185
}
185186
}
186187

188+
final releasedInVersion = lint.releasedInVersion;
189+
final unreleasedLint =
190+
lint.sinceDartSdk == 'Unreleased' ||
191+
lint.sinceDartSdk.contains('-wip') ||
192+
releasedInVersion == null ||
193+
releasedInVersion > runningDartVersion;
194+
187195
return Component.fragment(
188196
[
189197
Tags([
190-
if (lint.sinceDartSdk == 'Unreleased' ||
191-
lint.sinceDartSdk.contains('-wip'))
198+
if (unreleasedLint)
192199
const Tag(
193200
'Unreleased',
194201
icon: 'pending',
195-
color: 'orange',
202+
color: TagColor.orange,
196203
title: 'Lint is unreleased or work in progress.',
197204
)
198205
else if (lint.state == 'experimental')
199206
const Tag(
200207
'Experimental',
201208
icon: 'science',
202-
color: 'orange',
209+
color: TagColor.orange,
203210
title: 'Lint is experimental.',
204211
)
205212
else if (lint.state == 'deprecated')
206213
const Tag(
207214
'Deprecated',
208215
icon: 'report',
209-
color: 'orange',
216+
color: TagColor.orange,
210217
title: 'Lint is deprecated.',
211218
)
212219
else if (lint.state == 'removed')
213220
const Tag(
214221
'Removed',
215222
icon: 'error',
216-
color: 'red',
223+
color: TagColor.red,
217224
title: 'Lint has been removed.',
218225
)
219226
else
220227
const Tag(
221228
'Stable',
222229
icon: 'verified_user',
223-
color: 'green',
230+
color: TagColor.green,
224231
title: 'Lint is stable.',
225232
),
226233

@@ -250,6 +257,17 @@ List<MemoryPage> get _lintMemoryPages {
250257
icon: 'build',
251258
title: 'Lint has one or more quick fixes available.',
252259
),
260+
if (lint.sinceDartSdk != '2.0')
261+
Tag(
262+
'Released in Dart ${lint.sinceDartSdk}',
263+
icon: 'merge_type',
264+
color: TagColor.grey,
265+
title: unreleasedLint
266+
? 'This lint is set to be '
267+
'available in Dart ${lint.sinceDartSdk}'
268+
: 'This lint was originally '
269+
'available in Dart ${lint.sinceDartSdk}.',
270+
),
253271
]),
254272
DashMarkdown(
255273
content:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io' show Platform;
6+
7+
import 'package:pub_semver/pub_semver.dart' show Version;
8+
9+
/// The Dart version running this application.
10+
///
11+
/// Can only be called on the server, not from the client.
12+
final Version runningDartVersion = Version.parse(
13+
Platform.version.split(' ').first,
14+
);

0 commit comments

Comments
 (0)