Skip to content

Commit

Permalink
Support for workspace syntax in pubspec.yaml (#4128)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigurdm authored Feb 12, 2024
1 parent f8b2349 commit 3d285a3
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/src/language_version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class LanguageVersion implements Comparable<LanguageVersion> {

bool get supportsNullSafety => this >= firstVersionWithNullSafety;

bool get supportsWorkspaces => this >= firstVersionWithWorkspaces;

/// Minimum language version at which short hosted syntax is supported.
///
/// This allows `hosted` dependencies to be expressed as:
Expand Down Expand Up @@ -106,6 +108,8 @@ class LanguageVersion implements Comparable<LanguageVersion> {
static const defaultLanguageVersion = LanguageVersion(2, 7);
static const firstVersionWithNullSafety = LanguageVersion(2, 12);
static const firstVersionWithShorterHostedSyntax = LanguageVersion(2, 15);
// TODO(https://github.com/dart-lang/pub/issues/4127) update when we know actual version.
static const firstVersionWithWorkspaces = LanguageVersion(3, 7);

/// Transform language version to string that can be parsed with
/// [LanguageVersion.parse].
Expand Down
54 changes: 54 additions & 0 deletions lib/src/pubspec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,52 @@ class Pubspec extends PubspecBase {
/// is unknown.
Uri? get _location => fields.span.sourceUrl;

/// Directories of packages that should resolve together with this package.
late List<String> workspace = () {
final result = <String>[];
final r = fields.nodes['workspace'];
if (r != null && !languageVersion.supportsWorkspaces) {
_error(
'`workspace` and `resolution` requires at least language version ${LanguageVersion.firstVersionWithWorkspaces}',
r.span,
);
}
if (r == null || r.value == null) return <String>[];

if (r is! YamlList) {
_error('"workspace" must be a list of strings', r.span);
}
for (final t in r.nodes) {
final value = t.value;
if (value is! String) {
_error('"workspace" must be a list of strings', t.span);
}
result.add(value);
}
return result;
}();

/// The resolution mode.
late Resolution resolution = () {
final r = fields.nodes['resolution'];
if (r != null && !languageVersion.supportsWorkspaces) {
_error(
'`workspace` and `resolution` requires at least language version ${LanguageVersion.firstVersionWithWorkspaces}',
r.span,
);
}
return switch (r?.value) {
null => Resolution.none,
'local' => Resolution.local,
'workspace' => Resolution.workspace,
'external' => Resolution.external,
_ => _error(
'"resolution" must be one of `workspace`, `local`, `external`',
r!.span,
)
};
}();

/// The additional packages this package depends on.
Map<String, PackageRange> get dependencies =>
_dependencies ??= _parseDependencies(
Expand Down Expand Up @@ -253,6 +299,7 @@ class Pubspec extends PubspecBase {
Map? fields,
SourceRegistry? sources,
Map<String, SdkConstraint>? sdkConstraints,
this.workspace = const <String>[],
this.dependencyOverridesFromOverridesFile = false,
}) : _dependencies = dependencies == null
? null
Expand Down Expand Up @@ -727,3 +774,10 @@ class SdkConstraint {
@override
int get hashCode => Object.hash(effectiveConstraint, originalConstraint);
}

enum Resolution {
external,
workspace,
local,
none,
}
2 changes: 2 additions & 0 deletions lib/src/validator/pubspec_typo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ const _validPubspecKeys = [
'funding',
'topics',
'ignored_advisories',
'workspace',
'resolution',
];
95 changes: 95 additions & 0 deletions test/pubspec_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,101 @@ dependencies:
);
});

test('parses workspace', () {
expect(
Pubspec.parse(
'''
environment:
sdk: ^3.7.0
workspace: ['a', 'b', 'c']
''',
sources,
).workspace,
['a', 'b', 'c'],
);
});

test('parses resolution', () {
expect(
Pubspec.parse(
'''
environment:
sdk: ^3.7.0
resolution: workspace
''',
sources,
).resolution,
Resolution.workspace,
);
});

test('errors on workspace for earlier language versions', () {
expectPubspecException(
'''
environment:
sdk: ^1.2.3
workspace: ['a', 'b', 'c']
''',
(p) => p.workspace,
);
// but no error if you don't look at it.
expect(
Pubspec.parse(
'''
name: foo
environment:
sdk: ^1.2.3
resolution: workspace
''',
sources,
).name,
'foo',
);
});

test('errors on resolution for earlier language versions', () {
expectPubspecException(
'''
environment:
sdk: ^1.2.3
resolution: local
''',
(p) => p.resolution,
);
});

test('throws if workspace is not a list', () {
expectPubspecException(
'''
environment:
sdk: ^3.7.0
workspace: 'a string'
''',
(pubspec) => pubspec.workspace,
);
});

test('throws if workspace is a list of not-strings', () {
expectPubspecException(
'''
environment:
sdk: ^3.7.0
workspace: ['a string', 24]
''',
(pubspec) => pubspec.workspace,
);
});

test('throws if resolution is not a reasonable string', () {
expectPubspecException(
'''
environment:
sdk: ^3.7.0
resolution: "sometimes"''',
(pubspec) => pubspec.resolution,
);
});

test('allows comment-only files', () {
var pubspec = Pubspec.parse(
'''
Expand Down

0 comments on commit 3d285a3

Please sign in to comment.