diff --git a/json_serializable/CHANGELOG.md b/json_serializable/CHANGELOG.md index a0e2db27b..d8c5b5391 100644 --- a/json_serializable/CHANGELOG.md +++ b/json_serializable/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.9.3 + +- Error out if the target package does not have a language version of `3.0` or + greater. + ## 6.9.2 - Support the latest `package:analyzer`. diff --git a/json_serializable/lib/src/check_dependencies.dart b/json_serializable/lib/src/check_dependencies.dart index 7ea84bef4..13117f268 100644 --- a/json_serializable/lib/src/check_dependencies.dart +++ b/json_serializable/lib/src/check_dependencies.dart @@ -8,8 +8,12 @@ import 'package:build/build.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; +import 'constants.dart'; + const _productionDirectories = {'lib', 'bin'}; const _annotationPkgName = 'json_annotation'; +final _supportLanguageRange = + VersionConstraint.parse(supportedLanguageConstraint); final requiredJsonAnnotationMinVersion = Version.parse('4.9.0'); Future pubspecHasRightVersion(BuildStep buildStep) async { @@ -37,21 +41,38 @@ Future _validatePubspec(bool production, BuildStep buildStep) async { return; } - Future readPubspec(AssetId asset) async { - final string = await buildStep.readAsString(asset); - return Pubspec.parse(string, sourceUrl: asset.uri); + final string = await buildStep.readAsString(pubspecAssetId); + final pubspec = Pubspec.parse(string, sourceUrl: pubspecAssetId.uri); + + if (_checkAnnotationConstraint(pubspec, !production) + case final errorMessage?) { + log.warning(errorMessage); } - final pubspec = await readPubspec(pubspecAssetId); + // + // Ensure the current package language version is at least the minimum. + // + final currentPackageName = pubspec.name; + final packageConfig = await buildStep.packageConfig; + final thisPackage = packageConfig[currentPackageName]!; + + // build_runner will error out without an SDK version - so assuming + // `languageVersion` is not null. + final thisPackageVersion = thisPackage.languageVersion!; - final errorMessage = _checkAnnotationConstraint( - pubspec, - !production, - ); + final thisPackageVer = Version.parse('$thisPackageVersion.0'); + if (!_supportLanguageRange.allows(thisPackageVer)) { + log.warning( + ''' +The language version ($thisPackageVer) of this package ($currentPackageName) does not match the required range `$supportedLanguageConstraint`. - if (errorMessage == null) return; +Edit pubspec.yaml to include an SDK constraint of at least $supportedLanguageConstraint. - log.warning(errorMessage); +environment: + sdk: $supportedLanguageConstraint +''', + ); + } } String? _checkAnnotationConstraint( diff --git a/json_serializable/lib/src/constants.dart b/json_serializable/lib/src/constants.dart index a151ff034..70e85216f 100644 --- a/json_serializable/lib/src/constants.dart +++ b/json_serializable/lib/src/constants.dart @@ -13,3 +13,7 @@ const converterOrKeyInstructions = r''' * Use `JsonKey` fields `fromJson` and `toJson` https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html'''; + +/// This package generates code that uses case statements, which were introduced +/// in Dart 3.0. +const supportedLanguageConstraint = '^3.0.0'; diff --git a/json_serializable/pubspec.yaml b/json_serializable/pubspec.yaml index 4a3c00a20..80c9658aa 100644 --- a/json_serializable/pubspec.yaml +++ b/json_serializable/pubspec.yaml @@ -1,5 +1,5 @@ name: json_serializable -version: 6.9.2 +version: 6.9.3 description: >- Automatically generate code for converting to and from JSON by annotating Dart classes. diff --git a/json_serializable/test/annotation_version_test.dart b/json_serializable/test/annotation_version_test.dart index 1aed36547..607634ee1 100644 --- a/json_serializable/test/annotation_version_test.dart +++ b/json_serializable/test/annotation_version_test.dart @@ -10,6 +10,7 @@ library; import 'dart:io'; import 'package:json_serializable/src/check_dependencies.dart'; +import 'package:json_serializable/src/constants.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; @@ -20,7 +21,7 @@ import 'package:test_process/test_process.dart'; import 'test_utils.dart'; void main() { - test('validate pubspec constraint', () { + test('validate pubspec constraint', () async { final annotationConstraint = _jsonSerialPubspec.dependencies['json_annotation'] as HostedDependency; final versionRange = annotationConstraint.version as VersionRange; @@ -29,10 +30,35 @@ void main() { expect(versionRange.min, requiredJsonAnnotationMinVersion); }); + group('language version', () { + test('is less than required', () async { + const sdkLowerBound = '2.12.0'; + await _structurePackage( + environment: const {'sdk': '^$sdkLowerBound'}, + dependencies: {'json_annotation': _annotationLowerBound}, + message: ''' +The language version ($sdkLowerBound) of this package ($_testPkgName) does not match the required range `$supportedLanguageConstraint`. + +Edit pubspec.yaml to include an SDK constraint of at least $supportedLanguageConstraint. + +environment: + sdk: $supportedLanguageConstraint +''', + ); + }); + + test( + 'is at least the required `$supportedLanguageConstraint`', + () async => await _structurePackage( + dependencies: {'json_annotation': _annotationLowerBound}, + message: null, + ), + ); + }); + test( 'missing dependency in production code', () => _structurePackage( - sourceDirectory: 'lib', message: _missingProductionDep, ), ); @@ -50,7 +76,6 @@ void main() { test( 'dev dependency with a production usage', () => _structurePackage( - sourceDirectory: 'lib', devDependencies: {'json_annotation': _annotationLowerBound}, message: _missingProductionDep, ), @@ -59,7 +84,6 @@ void main() { test( 'dependency with `null` constraint', () => _structurePackage( - sourceDirectory: 'lib', dependencies: {'json_annotation': null}, message: 'The version constraint "any" on json_annotation allows versions ' @@ -70,7 +94,6 @@ void main() { test( 'dependency with "any" constraint', () => _structurePackage( - sourceDirectory: 'lib', dependencies: {'json_annotation': 'any'}, message: 'The version constraint "any" on json_annotation allows versions ' @@ -81,7 +104,6 @@ void main() { test( 'dependency with too low version range', () => _structurePackage( - sourceDirectory: 'lib', dependencies: {'json_annotation': '^4.0.0'}, message: 'The version constraint "^4.0.0" on json_annotation allows versions ' @@ -114,16 +136,19 @@ final _missingProductionDep = '"dependencies" section of your pubspec with a lower bound of at least ' '"$_annotationLowerBound".'; +const _testPkgName = '_test_pkg'; + Future _structurePackage({ - required String sourceDirectory, - required String message, + String sourceDirectory = 'lib', + required String? message, + Map environment = const {'sdk': supportedLanguageConstraint}, Map dependencies = const {}, Map devDependencies = const {}, }) async { final pubspec = loudEncode( { - 'name': '_test_pkg', - 'environment': {'sdk': '>=2.14.0 <3.0.0'}, + 'name': _testPkgName, + 'environment': environment, 'dependencies': dependencies, 'dev_dependencies': { ...devDependencies, @@ -162,9 +187,8 @@ class SomeClass{} ) ], ).create(); - final proc = await TestProcess.start( - 'dart', + Platform.resolvedExecutable, ['run', 'build_runner', 'build'], workingDirectory: d.sandbox, ); @@ -175,9 +199,22 @@ class SomeClass{} print(line); } - expect(lines.toString(), contains(''' + final output = lines.toString(); + final expectedWarningCount = message == null ? 0 : 1; + final warningCount = '[WARNING]'.allMatches(output).length; + expect( + warningCount, + expectedWarningCount, + reason: + 'Expected the number of output warnings ($warningCount) to match the ' + 'number of expected warnings ($expectedWarningCount).', + ); + + if (message != null) { + expect(output, contains(''' [WARNING] json_serializable on $sourceDirectory/sample.dart: $message''')); + } await proc.shouldExit(0); }