diff --git a/pkgs/pubspec_parse/CHANGELOG.md b/pkgs/pubspec_parse/CHANGELOG.md index 5aeb498020..5ead924a12 100644 --- a/pkgs/pubspec_parse/CHANGELOG.md +++ b/pkgs/pubspec_parse/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.6.0-wip + +- Supports 'tag_pattern's via `GitDependency.tagPattern`. + ## 1.5.0 - Added fields to `Pubspec`: `executables`, `resolution`, `workspace`. diff --git a/pkgs/pubspec_parse/lib/src/dependency.dart b/pkgs/pubspec_parse/lib/src/dependency.dart index 24c65eac1c..093ce24fa4 100644 --- a/pkgs/pubspec_parse/lib/src/dependency.dart +++ b/pkgs/pubspec_parse/lib/src/dependency.dart @@ -75,7 +75,7 @@ Dependency? _fromJson(Object? data, String name) { final key = matchedKeys.single; return switch (key) { - 'git' => GitDependency.fromData(data[key]), + 'git' => GitDependency.fromJson(data), 'path' => PathDependency.fromData(data[key]), 'sdk' => _$SdkDependencyFromJson(data), 'hosted' => _$HostedDependencyFromJson(data) @@ -112,25 +112,65 @@ class SdkDependency extends Dependency { String toString() => 'SdkDependency: $sdk'; } -@JsonSerializable() class GitDependency extends Dependency { @JsonKey(fromJson: parseGitUri) final Uri url; final String? ref; final String? path; - - GitDependency(this.url, {this.ref, this.path}); - - factory GitDependency.fromData(Object? data) { - if (data is String) { - data = {'url': data}; - } - - if (data is Map) { - return _$GitDependencyFromJson(data); - } - - throw ArgumentError.value(data, 'git', 'Must be a String or a Map.'); + final String? tagPattern; + final VersionConstraint? version; + + GitDependency(this.url, {this.ref, this.path, this.tagPattern, this.version}); + + factory GitDependency.fromJson(Map data) { + final version = switch (data['version']) { + final String? s => _constraintFromString(s), + _ => throw ArgumentError.value( + data['version'], + 'version', + 'if present must be a string.', + ), + }; + final gitData = switch (data['git']) { + final String s => {'url': s}, + final Map m => m, + _ => throw ArgumentError.value( + data['git'], + 'git', + 'Must be a string or map.', + ), + }; + final url = switch (gitData['url']) { + final String s => parseGitUri(s), + _ => + throw ArgumentError.value(gitData['url'], 'url', 'Must be a String.'), + }; + final ref = switch (gitData['ref']) { + final String? s => s, + _ => + throw ArgumentError.value(gitData['ref'], 'ref', 'Must be a String.'), + }; + final path = switch (gitData['path']) { + final String? s => s, + _ => + throw ArgumentError.value(gitData['path'], 'path', 'Must be a String.'), + }; + final tagPattern = switch (gitData['tag_pattern']) { + final String? s => s, + _ => throw ArgumentError.value( + gitData['tag_pattern'], + 'tag_pattern', + 'Must be a String.', + ), + }; + + return GitDependency( + url, + ref: ref, + path: path, + tagPattern: tagPattern, + version: version, + ); } @override diff --git a/pkgs/pubspec_parse/lib/src/dependency.g.dart b/pkgs/pubspec_parse/lib/src/dependency.g.dart index 1a504f1fdf..11ce96af54 100644 --- a/pkgs/pubspec_parse/lib/src/dependency.g.dart +++ b/pkgs/pubspec_parse/lib/src/dependency.g.dart @@ -21,19 +21,6 @@ SdkDependency _$SdkDependencyFromJson(Map json) => $checkedCreate( }, ); -GitDependency _$GitDependencyFromJson(Map json) => $checkedCreate( - 'GitDependency', - json, - ($checkedConvert) { - final val = GitDependency( - $checkedConvert('url', (v) => parseGitUri(v as String)), - ref: $checkedConvert('ref', (v) => v as String?), - path: $checkedConvert('path', (v) => v as String?), - ); - return val; - }, - ); - HostedDependency _$HostedDependencyFromJson(Map json) => $checkedCreate( 'HostedDependency', json, diff --git a/pkgs/pubspec_parse/pubspec.yaml b/pkgs/pubspec_parse/pubspec.yaml index 90741efffa..4a5f4568fa 100644 --- a/pkgs/pubspec_parse/pubspec.yaml +++ b/pkgs/pubspec_parse/pubspec.yaml @@ -1,5 +1,5 @@ name: pubspec_parse -version: 1.5.0 +version: 1.6.0-wip description: >- Simple package for parsing pubspec.yaml files with a type-safe API and rich error reporting. diff --git a/pkgs/pubspec_parse/test/dependency_test.dart b/pkgs/pubspec_parse/test/dependency_test.dart index f1e4f57760..6b4f3046ab 100644 --- a/pkgs/pubspec_parse/test/dependency_test.dart +++ b/pkgs/pubspec_parse/test/dependency_test.dart @@ -258,6 +258,25 @@ void _gitDependency() { expect(dep.toString(), 'GitDependency: url@url'); }); + test('tag_pattern works, and does not ignore version', () async { + final dep = await _dependency( + { + 'git': { + 'url': 'url', + 'tag_pattern': 'v{{version}}', + }, + 'version': '^1.2.3', + }, + languageVersion: '3.9', + ); + expect(dep.url.toString(), 'url'); + expect(dep.path, isNull); + expect(dep.ref, isNull); + expect(dep.tagPattern, 'v{{version}}'); + expect(dep.version.toString(), '^1.2.3'); + expect(dep.toString(), 'GitDependency: url@url'); + }); + test('string with user@ URL', () async { final skipTryParse = Platform.environment.containsKey('TRAVIS'); if (skipTryParse) { @@ -299,7 +318,7 @@ line 6, column 4: Unrecognized keys: [bob]; supported keys: [sdk, git, path, hos _expectThrows( {'git': null}, r''' -line 5, column 11: Unsupported value for "git". Must be a String or a Map. +line 5, column 11: Unsupported value for "git". Must be a string or map. ╷ 5 │ "git": null │ ┌───────────^ @@ -313,7 +332,7 @@ line 5, column 11: Unsupported value for "git". Must be a String or a Map. _expectThrows( {'git': 42}, r''' -line 5, column 11: Unsupported value for "git". Must be a String or a Map. +line 5, column 11: Unsupported value for "git". Must be a string or map. ╷ 5 │ "git": 42 │ ┌───────────^ @@ -326,7 +345,7 @@ line 5, column 11: Unsupported value for "git". Must be a String or a Map. test('git - empty map', () { _expectThrowsContaining( {'git': {}}, - r"type 'Null' is not a subtype of type 'String'", + r'Missing key "url". Must be a String', ); }); @@ -335,7 +354,7 @@ line 5, column 11: Unsupported value for "git". Must be a String or a Map. { 'git': {'url': null}, }, - r"type 'Null' is not a subtype of type 'String'", + r'Missing key "url". Must be a String', ); }); @@ -344,7 +363,7 @@ line 5, column 11: Unsupported value for "git". Must be a String or a Map. { 'git': {'url': 42}, }, - r"type 'int' is not a subtype of type 'String'", + r'Missing key "url". Must be a String', ); }); } @@ -428,10 +447,11 @@ void _expectThrowsContaining(Object content, String errorText) { Future _dependency( Object? content, { bool skipTryPub = false, + String languageVersion = '2.12', }) async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(languageVersion: languageVersion), 'dependencies': {'dep': content}, }, skipTryPub: skipTryPub, diff --git a/pkgs/pubspec_parse/test/parse_test.dart b/pkgs/pubspec_parse/test/parse_test.dart index e0698af163..185a6e82ae 100644 --- a/pkgs/pubspec_parse/test/parse_test.dart +++ b/pkgs/pubspec_parse/test/parse_test.dart @@ -12,7 +12,7 @@ import 'test_utils.dart'; void main() { test('minimal set values', () async { - final value = await parse(defaultPubspec); + final value = await parse(defaultPubspec()); expect(value.name, 'sample'); expect(value.version, isNull); expect(value.publishTo, isNull); @@ -167,7 +167,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https }.entries) { test('can be ${entry.key}', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'publish_to': entry.value, }); expect(value.publishTo, entry.value); @@ -178,7 +178,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https group('author, authors', () { test('one author', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'author': 'name@example.com', }); expect(value.author, 'name@example.com'); @@ -187,7 +187,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('one author, via authors', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'authors': ['name@example.com'], }); expect(value.author, 'name@example.com'); @@ -196,7 +196,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('many authors', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'authors': ['name@example.com', 'name2@example.com'], }); expect(value.author, isNull); @@ -205,7 +205,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('author and authors', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'author': 'name@example.com', 'authors': ['name2@example.com'], }); @@ -215,7 +215,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('duplicate author values', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'author': 'name@example.com', 'authors': ['name@example.com', 'name@example.com'], }); @@ -225,7 +225,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('flutter', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'flutter': {'key': 'value'}, }); expect(value.flutter, {'key': 'value'}); @@ -235,7 +235,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https group('executables', () { test('one executable', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'executables': {'my_script': 'bin/my_script.dart'}, }); expect(value.executables, hasLength(1)); @@ -245,7 +245,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('many executables', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'executables': { 'my_script': 'bin/my_script.dart', 'my_script2': 'bin/my_script2.dart', @@ -261,7 +261,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('invalid value', () async { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'executables': { 'script': 32, }, @@ -274,7 +274,7 @@ line 3, column 16: Unsupported value for "publish_to". Must be an http or https test('invalid executable - lenient', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'executables': 'Invalid value', }, lenient: true, @@ -407,7 +407,7 @@ line 4, column 10: Unsupported value for "sdk". Could not parse version "silly". test('bad repository url', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'repository': {'x': 'y'}, }, "Unsupported value for \"repository\". type 'YamlMap' is not a subtype of type 'String'", @@ -431,7 +431,7 @@ line 4, column 10: Unsupported value for "sdk". Could not parse version "silly". test('not a list', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'funding': 1, }, "Unsupported value for \"funding\". type 'int' is not a subtype of type 'List?'", @@ -442,7 +442,7 @@ line 4, column 10: Unsupported value for "sdk". Could not parse version "silly". test('not an uri', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'funding': [1], }, "Unsupported value for \"funding\". type 'int' is not a subtype of type 'String'", @@ -453,7 +453,7 @@ line 4, column 10: Unsupported value for "sdk". Could not parse version "silly". test('not an uri', () { expectParseThrows( { - ...defaultPubspec, + ...defaultPubspec(), 'funding': ['ht tps://example.com/'], }, r''' @@ -472,7 +472,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('not a list', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'topics': 1, }, "Unsupported value for \"topics\". type 'int' is not a subtype of type 'List?'", @@ -483,7 +483,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('not a string', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'topics': [1], }, "Unsupported value for \"topics\". type 'int' is not a subtype of type 'String'", @@ -494,7 +494,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('invalid data - lenient', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'topics': [1], }, skipTryPub: true, @@ -509,7 +509,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('not a list', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'ignored_advisories': 1, }, "Unsupported value for \"ignored_advisories\". type 'int' is not a subtype of type 'List?'", @@ -520,7 +520,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('not a string', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'ignored_advisories': [1], }, "Unsupported value for \"ignored_advisories\". type 'int' is not a subtype of type 'String'", @@ -531,7 +531,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('invalid data - lenient', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'ignored_advisories': [1], }, skipTryPub: true, @@ -545,7 +545,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at group('screenshots', () { test('one screenshot', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ {'description': 'my screenshot', 'path': 'path/to/screenshot'}, ], @@ -557,7 +557,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('many screenshots', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ {'description': 'my screenshot', 'path': 'path/to/screenshot'}, { @@ -575,7 +575,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('one screenshot plus invalid entries', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ 42, { @@ -593,7 +593,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('invalid entries', () async { final value = await parse({ - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ 42, 'not a screenshot', @@ -605,7 +605,7 @@ line 6, column 13: Unsupported value for "funding". Illegal scheme character at test('missing key `dessription', () { expectParseThrows( { - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ {'path': 'my/path'}, ], @@ -624,7 +624,7 @@ line 7, column 3: Missing key "description". Missing required key `description` test('missing key `path`', () { expectParseThrows( { - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ {'description': 'my screenshot'}, ], @@ -643,7 +643,7 @@ line 7, column 3: Missing key "path". Missing required key `path` test('Value of description not a String`', () { expectParseThrows( { - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ {'description': 42}, ], @@ -663,7 +663,7 @@ line 8, column 19: Unsupported value for "description". `42` is not a String test('Value of path not a String`', () { expectParseThrows( { - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': [ { 'description': '', @@ -686,7 +686,7 @@ line 9, column 12: Unsupported value for "path". `42` is not a String test('invalid screenshot - lenient', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'screenshots': 'Invalid value', }, lenient: true, @@ -734,7 +734,7 @@ line 1, column 1: Not a map test('bad repository url', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'repository': {'x': 'y'}, }, lenient: true, @@ -746,7 +746,7 @@ line 1, column 1: Not a map test('bad issue_tracker url', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'issue_tracker': {'x': 'y'}, }, lenient: true, @@ -758,7 +758,7 @@ line 1, column 1: Not a map test('multiple bad values', () async { final value = await parse( { - ...defaultPubspec, + ...defaultPubspec(), 'repository': {'x': 'y'}, 'issue_tracker': {'x': 'y'}, }, @@ -793,7 +793,7 @@ line 1, column 1: Not a map test('workspace key must be a list', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'workspace': 42, }, 'Unsupported value for "workspace". type \'int\' is not a subtype of type \'List?\' in type cast', @@ -804,7 +804,7 @@ line 1, column 1: Not a map test('workspace key must be a list of strings', () { expectParseThrowsContaining( { - ...defaultPubspec, + ...defaultPubspec(), 'workspace': [42], }, 'Unsupported value for "workspace". type \'int\' is not a subtype of type \'String\' in type cast', diff --git a/pkgs/pubspec_parse/test/test_utils.dart b/pkgs/pubspec_parse/test/test_utils.dart index cc46522b7e..9b3179cd89 100644 --- a/pkgs/pubspec_parse/test/test_utils.dart +++ b/pkgs/pubspec_parse/test/test_utils.dart @@ -12,10 +12,10 @@ import 'package:test/test.dart'; import 'pub_utils.dart'; -const defaultPubspec = { - 'name': 'sample', - 'environment': {'sdk': '>=2.12.0 <3.0.0'}, -}; +Map defaultPubspec({String? languageVersion = '2.12'}) => { + 'name': 'sample', + 'environment': {'sdk': '^$languageVersion.0'}, + }; String _encodeJson(Object? input) => const JsonEncoder.withIndent(' ').convert(input);