Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for workspace syntax in pubspec.yaml #4128

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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
Loading