Added `workspace:` and `resolution: fields` (#1948)

Co-authored-by: Kevin Moore <kevmoo@users.noreply.github.com>
diff --git a/pkgs/pubspec_parse/CHANGELOG.md b/pkgs/pubspec_parse/CHANGELOG.md
index a5f0f1a..3aedaad 100644
--- a/pkgs/pubspec_parse/CHANGELOG.md
+++ b/pkgs/pubspec_parse/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.5.0-wip
+
+- Add `Pubspec.workspace` and `Pubspec.resolution` fields.
+
 ## 1.4.0
 
 - Require Dart 3.2
@@ -31,7 +35,7 @@
 - Added support for `screenshots` field.
 - Update `HostedDetails` to reflect how `hosted` dependencies are parsed in
   Dart 2.15:
-   - Add `HostedDetails.declaredName` as the (optional) `name` property in a 
+   - Add `HostedDetails.declaredName` as the (optional) `name` property in a
      `hosted` block.
    - `HostedDetails.name` now falls back to the name of the dependency if no
       name is declared in the block.
diff --git a/pkgs/pubspec_parse/lib/src/pubspec.dart b/pkgs/pubspec_parse/lib/src/pubspec.dart
index 1317a23..cdfc8ff 100644
--- a/pkgs/pubspec_parse/lib/src/pubspec.dart
+++ b/pkgs/pubspec_parse/lib/src/pubspec.dart
@@ -93,6 +93,12 @@
   /// and other settings.
   final Map<String, dynamic>? flutter;
 
+  /// If this package is a Pub Workspace, this field lists the sub-packages.
+  final List<String>? workspace;
+
+  /// Specifies how to resolve dependencies with the surrounding Pub Workspace.
+  final String? resolution;
+
   /// If [author] and [authors] are both provided, their values are combined
   /// with duplicates eliminated.
   Pubspec(
@@ -117,6 +123,8 @@
     this.screenshots,
     this.documentation,
     this.description,
+    this.workspace,
+    this.resolution,
     Map<String, Dependency>? dependencies,
     Map<String, Dependency>? devDependencies,
     Map<String, Dependency>? dependencyOverrides,
diff --git a/pkgs/pubspec_parse/lib/src/pubspec.g.dart b/pkgs/pubspec_parse/lib/src/pubspec.g.dart
index fc28571..f0fb79e 100644
--- a/pkgs/pubspec_parse/lib/src/pubspec.g.dart
+++ b/pkgs/pubspec_parse/lib/src/pubspec.g.dart
@@ -40,6 +40,9 @@
               'screenshots', (v) => parseScreenshots(v as List?)),
           documentation: $checkedConvert('documentation', (v) => v as String?),
           description: $checkedConvert('description', (v) => v as String?),
+          workspace: $checkedConvert('workspace',
+              (v) => (v as List<dynamic>?)?.map((e) => e as String).toList()),
+          resolution: $checkedConvert('resolution', (v) => v as String?),
           dependencies:
               $checkedConvert('dependencies', (v) => parseDeps(v as Map?)),
           devDependencies:
diff --git a/pkgs/pubspec_parse/pubspec.yaml b/pkgs/pubspec_parse/pubspec.yaml
index ad0c55e..476bbb8 100644
--- a/pkgs/pubspec_parse/pubspec.yaml
+++ b/pkgs/pubspec_parse/pubspec.yaml
@@ -1,5 +1,5 @@
 name: pubspec_parse
-version: 1.4.0
+version: 1.5.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/parse_test.dart b/pkgs/pubspec_parse/test/parse_test.dart
index 6251f41..5570b60 100644
--- a/pkgs/pubspec_parse/test/parse_test.dart
+++ b/pkgs/pubspec_parse/test/parse_test.dart
@@ -32,31 +32,41 @@
     expect(value.repository, isNull);
     expect(value.issueTracker, isNull);
     expect(value.screenshots, isEmpty);
+    expect(value.workspace, isNull);
+    expect(value.resolution, isNull);
   });
 
   test('all fields set', () async {
     final version = Version.parse('1.2.3');
-    final sdkConstraint = VersionConstraint.parse('>=2.12.0 <3.0.0');
-    final value = await parse({
-      'name': 'sample',
-      'version': version.toString(),
-      'publish_to': 'none',
-      'author': 'name@example.com',
-      'environment': {'sdk': sdkConstraint.toString()},
-      'description': 'description',
-      'homepage': 'homepage',
-      'documentation': 'documentation',
-      'repository': 'https://github.com/example/repo',
-      'issue_tracker': 'https://github.com/example/repo/issues',
-      'funding': [
-        'https://patreon.com/example',
-      ],
-      'topics': ['widget', 'button'],
-      'ignored_advisories': ['111', '222'],
-      'screenshots': [
-        {'description': 'my screenshot', 'path': 'path/to/screenshot'},
-      ],
-    });
+    final sdkConstraint = VersionConstraint.parse('>=3.6.0 <4.0.0');
+    final value = await parse(
+      {
+        'name': 'sample',
+        'version': version.toString(),
+        'publish_to': 'none',
+        'author': 'name@example.com',
+        'environment': {'sdk': sdkConstraint.toString()},
+        'description': 'description',
+        'homepage': 'homepage',
+        'documentation': 'documentation',
+        'repository': 'https://github.com/example/repo',
+        'issue_tracker': 'https://github.com/example/repo/issues',
+        'funding': [
+          'https://patreon.com/example',
+        ],
+        'topics': ['widget', 'button'],
+        'ignored_advisories': ['111', '222'],
+        'screenshots': [
+          {'description': 'my screenshot', 'path': 'path/to/screenshot'},
+        ],
+        'workspace': [
+          'pkg1',
+          'pkg2',
+        ],
+        'resolution': 'workspace',
+      },
+      skipTryPub: true,
+    );
     expect(value.name, 'sample');
     expect(value.version, version);
     expect(value.publishTo, 'none');
@@ -86,6 +96,10 @@
     expect(value.screenshots, hasLength(1));
     expect(value.screenshots!.first.description, 'my screenshot');
     expect(value.screenshots!.first.path, 'path/to/screenshot');
+    expect(value.workspace, hasLength(2));
+    expect(value.workspace!.first, 'pkg1');
+    expect(value.workspace!.last, 'pkg2');
+    expect(value.resolution, 'workspace');
   });
 
   test('environment values can be null', () async {
@@ -712,4 +726,40 @@
       );
     });
   });
+
+  group('workspaces', () {
+    test('workspace key must be a list', () {
+      expectParseThrowsContaining(
+        {
+          ...defaultPubspec,
+          'workspace': 42,
+        },
+        'Unsupported value for "workspace". type \'int\' is not a subtype of type \'List<dynamic>?\' in type cast',
+        skipTryPub: true,
+      );
+    });
+
+    test('workspace key must be a list of strings', () {
+      expectParseThrowsContaining(
+        {
+          ...defaultPubspec,
+          'workspace': [42],
+        },
+        'Unsupported value for "workspace". type \'int\' is not a subtype of type \'String\' in type cast',
+        skipTryPub: true,
+      );
+    });
+
+    test('resolution key must be a string', () {
+      expectParseThrowsContaining(
+        {
+          'name': 'sample',
+          'environment': {'sdk': '^3.6.0'},
+          'resolution': 42,
+        },
+        'Unsupported value for "resolution". type \'int\' is not a subtype of type \'String?\' in type cast',
+        skipTryPub: true,
+      );
+    });
+  });
 }