Skip to content

Commit 3dd6767

Browse files
authored
Error out if the language version of the target package is too low (#1474)
Much be at least 3.0 since we generate code with case statements Fixes #1462
1 parent 3318916 commit 3dd6767

File tree

5 files changed

+91
-24
lines changed

5 files changed

+91
-24
lines changed

json_serializable/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 6.9.3
2+
3+
- Error out if the target package does not have a language version of `3.0` or
4+
greater.
5+
16
## 6.9.2
27

38
- Support the latest `package:analyzer`.

json_serializable/lib/src/check_dependencies.dart

+31-10
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ import 'package:build/build.dart';
88
import 'package:pub_semver/pub_semver.dart';
99
import 'package:pubspec_parse/pubspec_parse.dart';
1010

11+
import 'constants.dart';
12+
1113
const _productionDirectories = {'lib', 'bin'};
1214
const _annotationPkgName = 'json_annotation';
15+
final _supportLanguageRange =
16+
VersionConstraint.parse(supportedLanguageConstraint);
1317
final requiredJsonAnnotationMinVersion = Version.parse('4.9.0');
1418

1519
Future<void> pubspecHasRightVersion(BuildStep buildStep) async {
@@ -37,21 +41,38 @@ Future<void> _validatePubspec(bool production, BuildStep buildStep) async {
3741
return;
3842
}
3943

40-
Future<Pubspec> readPubspec(AssetId asset) async {
41-
final string = await buildStep.readAsString(asset);
42-
return Pubspec.parse(string, sourceUrl: asset.uri);
44+
final string = await buildStep.readAsString(pubspecAssetId);
45+
final pubspec = Pubspec.parse(string, sourceUrl: pubspecAssetId.uri);
46+
47+
if (_checkAnnotationConstraint(pubspec, !production)
48+
case final errorMessage?) {
49+
log.warning(errorMessage);
4350
}
4451

45-
final pubspec = await readPubspec(pubspecAssetId);
52+
//
53+
// Ensure the current package language version is at least the minimum.
54+
//
55+
final currentPackageName = pubspec.name;
56+
final packageConfig = await buildStep.packageConfig;
57+
final thisPackage = packageConfig[currentPackageName]!;
58+
59+
// build_runner will error out without an SDK version - so assuming
60+
// `languageVersion` is not null.
61+
final thisPackageVersion = thisPackage.languageVersion!;
4662

47-
final errorMessage = _checkAnnotationConstraint(
48-
pubspec,
49-
!production,
50-
);
63+
final thisPackageVer = Version.parse('$thisPackageVersion.0');
64+
if (!_supportLanguageRange.allows(thisPackageVer)) {
65+
log.warning(
66+
'''
67+
The language version ($thisPackageVer) of this package ($currentPackageName) does not match the required range `$supportedLanguageConstraint`.
5168
52-
if (errorMessage == null) return;
69+
Edit pubspec.yaml to include an SDK constraint of at least $supportedLanguageConstraint.
5370
54-
log.warning(errorMessage);
71+
environment:
72+
sdk: $supportedLanguageConstraint
73+
''',
74+
);
75+
}
5576
}
5677

5778
String? _checkAnnotationConstraint(

json_serializable/lib/src/constants.dart

+4
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ const converterOrKeyInstructions = r'''
1313
* Use `JsonKey` fields `fromJson` and `toJson`
1414
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
1515
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html''';
16+
17+
/// This package generates code that uses case statements, which were introduced
18+
/// in Dart 3.0.
19+
const supportedLanguageConstraint = '^3.0.0';

json_serializable/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: json_serializable
2-
version: 6.9.2
2+
version: 6.9.3
33
description: >-
44
Automatically generate code for converting to and from JSON by annotating
55
Dart classes.

json_serializable/test/annotation_version_test.dart

+50-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ library;
1010
import 'dart:io';
1111

1212
import 'package:json_serializable/src/check_dependencies.dart';
13+
import 'package:json_serializable/src/constants.dart';
1314
import 'package:path/path.dart' as p;
1415
import 'package:pub_semver/pub_semver.dart';
1516
import 'package:pubspec_parse/pubspec_parse.dart';
@@ -20,7 +21,7 @@ import 'package:test_process/test_process.dart';
2021
import 'test_utils.dart';
2122

2223
void main() {
23-
test('validate pubspec constraint', () {
24+
test('validate pubspec constraint', () async {
2425
final annotationConstraint =
2526
_jsonSerialPubspec.dependencies['json_annotation'] as HostedDependency;
2627
final versionRange = annotationConstraint.version as VersionRange;
@@ -29,10 +30,35 @@ void main() {
2930
expect(versionRange.min, requiredJsonAnnotationMinVersion);
3031
});
3132

33+
group('language version', () {
34+
test('is less than required', () async {
35+
const sdkLowerBound = '2.12.0';
36+
await _structurePackage(
37+
environment: const {'sdk': '^$sdkLowerBound'},
38+
dependencies: {'json_annotation': _annotationLowerBound},
39+
message: '''
40+
The language version ($sdkLowerBound) of this package ($_testPkgName) does not match the required range `$supportedLanguageConstraint`.
41+
42+
Edit pubspec.yaml to include an SDK constraint of at least $supportedLanguageConstraint.
43+
44+
environment:
45+
sdk: $supportedLanguageConstraint
46+
''',
47+
);
48+
});
49+
50+
test(
51+
'is at least the required `$supportedLanguageConstraint`',
52+
() async => await _structurePackage(
53+
dependencies: {'json_annotation': _annotationLowerBound},
54+
message: null,
55+
),
56+
);
57+
});
58+
3259
test(
3360
'missing dependency in production code',
3461
() => _structurePackage(
35-
sourceDirectory: 'lib',
3662
message: _missingProductionDep,
3763
),
3864
);
@@ -50,7 +76,6 @@ void main() {
5076
test(
5177
'dev dependency with a production usage',
5278
() => _structurePackage(
53-
sourceDirectory: 'lib',
5479
devDependencies: {'json_annotation': _annotationLowerBound},
5580
message: _missingProductionDep,
5681
),
@@ -59,7 +84,6 @@ void main() {
5984
test(
6085
'dependency with `null` constraint',
6186
() => _structurePackage(
62-
sourceDirectory: 'lib',
6387
dependencies: {'json_annotation': null},
6488
message:
6589
'The version constraint "any" on json_annotation allows versions '
@@ -70,7 +94,6 @@ void main() {
7094
test(
7195
'dependency with "any" constraint',
7296
() => _structurePackage(
73-
sourceDirectory: 'lib',
7497
dependencies: {'json_annotation': 'any'},
7598
message:
7699
'The version constraint "any" on json_annotation allows versions '
@@ -81,7 +104,6 @@ void main() {
81104
test(
82105
'dependency with too low version range',
83106
() => _structurePackage(
84-
sourceDirectory: 'lib',
85107
dependencies: {'json_annotation': '^4.0.0'},
86108
message:
87109
'The version constraint "^4.0.0" on json_annotation allows versions '
@@ -114,16 +136,19 @@ final _missingProductionDep =
114136
'"dependencies" section of your pubspec with a lower bound of at least '
115137
'"$_annotationLowerBound".';
116138

139+
const _testPkgName = '_test_pkg';
140+
117141
Future<void> _structurePackage({
118-
required String sourceDirectory,
119-
required String message,
142+
String sourceDirectory = 'lib',
143+
required String? message,
144+
Map<String, dynamic> environment = const {'sdk': supportedLanguageConstraint},
120145
Map<String, dynamic> dependencies = const {},
121146
Map<String, dynamic> devDependencies = const {},
122147
}) async {
123148
final pubspec = loudEncode(
124149
{
125-
'name': '_test_pkg',
126-
'environment': {'sdk': '>=2.14.0 <3.0.0'},
150+
'name': _testPkgName,
151+
'environment': environment,
127152
'dependencies': dependencies,
128153
'dev_dependencies': {
129154
...devDependencies,
@@ -162,9 +187,8 @@ class SomeClass{}
162187
)
163188
],
164189
).create();
165-
166190
final proc = await TestProcess.start(
167-
'dart',
191+
Platform.resolvedExecutable,
168192
['run', 'build_runner', 'build'],
169193
workingDirectory: d.sandbox,
170194
);
@@ -175,9 +199,22 @@ class SomeClass{}
175199
print(line);
176200
}
177201

178-
expect(lines.toString(), contains('''
202+
final output = lines.toString();
203+
final expectedWarningCount = message == null ? 0 : 1;
204+
final warningCount = '[WARNING]'.allMatches(output).length;
205+
expect(
206+
warningCount,
207+
expectedWarningCount,
208+
reason:
209+
'Expected the number of output warnings ($warningCount) to match the '
210+
'number of expected warnings ($expectedWarningCount).',
211+
);
212+
213+
if (message != null) {
214+
expect(output, contains('''
179215
[WARNING] json_serializable on $sourceDirectory/sample.dart:
180216
$message'''));
217+
}
181218

182219
await proc.shouldExit(0);
183220
}

0 commit comments

Comments
 (0)