Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions json_serializable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
39 changes: 29 additions & 10 deletions json_serializable/lib/src/check_dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(supportLanguageConstraint);
final requiredJsonAnnotationMinVersion = Version.parse('4.9.0');

Future<void> pubspecHasRightVersion(BuildStep buildStep) async {
Expand Down Expand Up @@ -37,21 +41,36 @@ Future<void> _validatePubspec(bool production, BuildStep buildStep) async {
return;
}

Future<Pubspec> readPubspec(AssetId asset) async {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No clue why I had an inline function called once. Ditched it.

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)
Copy link
Collaborator Author

@kevmoo kevmoo Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seemed the cleanest way to call the function and "do the thing" if the result is not null.

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 errorMessage = _checkAnnotationConstraint(
pubspec,
!production,
);
final thisPackage = packageConfig[currentPackageName]!;

if (errorMessage == null) return;
// build_runner will error out without an SDK version - so this "should" be
// fine.
final thisPackageVersion = thisPackage.languageVersion!;

log.warning(errorMessage);
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 '
'`$supportLanguageConstraint`.',
);
}
}

String? _checkAnnotationConstraint(
Expand Down
4 changes: 4 additions & 0 deletions json_serializable/lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 supportLanguageConstraint = '^3.0.0';
2 changes: 1 addition & 1 deletion json_serializable/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
92 changes: 64 additions & 28 deletions json_serializable/test/annotation_version_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -29,63 +30,84 @@ 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},
warningMessages: [
'The language version ($sdkLowerBound) of this package '
'($_testPkgName) does not match the required range '
'`$supportLanguageConstraint`.'
],
);
});

test('is at least the required `$supportLanguageConstraint`', () async {
await _structurePackage(
dependencies: {'json_annotation': _annotationLowerBound},
warningMessages: [],
);
});
});

test(
'missing dependency in production code',
() => _structurePackage(
sourceDirectory: 'lib',
message: _missingProductionDep,
warningMessages: [_missingProductionDep],
),
);

test(
'missing dependency in example code',
() => _structurePackage(
sourceDirectory: 'example',
message:
'You are missing a required dependency on json_annotation with a '
'lower bound of at least "$_annotationLowerBound".',
warningMessages: [
'You are missing a required dependency on json_annotation with a '
'lower bound of at least "$_annotationLowerBound".'
],
),
);

test(
'dev dependency with a production usage',
() => _structurePackage(
sourceDirectory: 'lib',
devDependencies: {'json_annotation': _annotationLowerBound},
message: _missingProductionDep,
warningMessages: [_missingProductionDep],
),
);

test(
'dependency with `null` constraint',
() => _structurePackage(
sourceDirectory: 'lib',
dependencies: {'json_annotation': null},
message:
'The version constraint "any" on json_annotation allows versions '
'before $_annotationLowerBound which is not allowed.',
warningMessages: [
'The version constraint "any" on json_annotation allows versions '
'before $_annotationLowerBound which is not allowed.'
],
),
);

test(
'dependency with "any" constraint',
() => _structurePackage(
sourceDirectory: 'lib',
dependencies: {'json_annotation': 'any'},
message:
'The version constraint "any" on json_annotation allows versions '
'before $_annotationLowerBound which is not allowed.',
warningMessages: [
'The version constraint "any" on json_annotation allows versions '
'before $_annotationLowerBound which is not allowed.'
],
),
);

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 '
'before $_annotationLowerBound which is not allowed.',
warningMessages: [
'The version constraint "^4.0.0" on json_annotation allows versions '
'before $_annotationLowerBound which is not allowed.'
],
),
);
}
Expand Down Expand Up @@ -114,16 +136,19 @@ final _missingProductionDep =
'"dependencies" section of your pubspec with a lower bound of at least '
'"$_annotationLowerBound".';

const _testPkgName = '_test_pkg';

Future<void> _structurePackage({
required String sourceDirectory,
required String message,
String sourceDirectory = 'lib',
required List<String> warningMessages,
Map<String, dynamic> environment = const {'sdk': supportLanguageConstraint},
Map<String, dynamic> dependencies = const {},
Map<String, dynamic> 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,
Expand Down Expand Up @@ -162,9 +187,8 @@ class SomeClass{}
)
],
).create();

final proc = await TestProcess.start(
'dart',
Platform.resolvedExecutable,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests should run on windows now!

['run', 'build_runner', 'build'],
workingDirectory: d.sandbox,
);
Expand All @@ -175,9 +199,21 @@ class SomeClass{}
print(line);
}

expect(lines.toString(), contains('''
final output = lines.toString();
final warningCount = '[WARNING]'.allMatches(output).length;
expect(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure there are only the warnings we are expecting and no more!

warningCount,
warningMessages.length,
reason:
'Expected the number of output warnings ($warningCount) to match the '
'number of expected warnings (${warningMessages.length}.',
);

for (var warningMessage in warningMessages) {
expect(output, contains('''
[WARNING] json_serializable on $sourceDirectory/sample.dart:
$message'''));
$warningMessage'''));
}

await proc.shouldExit(0);
}
Loading