diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart index a9315fb4d..562d6af9b 100644 --- a/lib/src/entrypoint.dart +++ b/lib/src/entrypoint.dart @@ -123,7 +123,7 @@ class Entrypoint { ); for (final package in root.transitiveWorkspace) { if (identical(pubspecsMet.entries.first.value, package.pubspec)) { - validateWorkspaceGraph(root); + validateWorkspace(root); return (root: root, work: package); } } diff --git a/lib/src/package.dart b/lib/src/package.dart index e1c520880..1cf303460 100644 --- a/lib/src/package.dart +++ b/lib/src/package.dart @@ -361,8 +361,10 @@ See $workspacesDocUrl for more information. } /// Reports an error if the graph of the workspace rooted at [root] is not a -/// tree. -void validateWorkspaceGraph(Package root) { +/// tree. Or if a package name occurs twice. +void validateWorkspace(Package root) { + if (root.workspaceChildren.isEmpty) return; + final includedFrom = {}; final stack = [root]; @@ -382,4 +384,17 @@ Packages can only be included in the workspace once. } stack.addAll(current.workspaceChildren); } + + // Check that the workspace doesn't contain two packages with the same name! + final namesSeen = {}; + for (final package in root.transitiveWorkspace) { + final collision = namesSeen[package.name]; + if (collision != null) { + fail(''' +Workspace members must have unique names. +`${collision.pubspecPath}` and `${package.pubspecPath}` are both called "${package.name}". +'''); + } + namesSeen[package.name] = package; + } } diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart index 5c401236c..d10990f43 100644 --- a/lib/src/package_config.dart +++ b/lib/src/package_config.dart @@ -50,7 +50,17 @@ class PackageConfig { this.generator, this.generatorVersion, Map? additionalProperties, - }) : additionalProperties = additionalProperties ?? {}; + }) : additionalProperties = additionalProperties ?? {} { + final names = {}; + // Sanity check: + for (final p in packages) { + if (!names.add(p.name)) { + throw ArgumentError( + 'Duplicate name ${p.name} in generated package config', + ); + } + } + } /// Create [PackageConfig] from JSON [data]. /// diff --git a/test/workspace_test.dart b/test/workspace_test.dart index e381fb5f9..d0fa09d9a 100644 --- a/test/workspace_test.dart +++ b/test/workspace_test.dart @@ -1163,6 +1163,41 @@ Changed 1 constraint in b${s}pubspec.yaml: 'Because myapp depends on both a 2.0.0 and a, version solving failed.', ); }); + + test('Reports error if two members of workspace has same name', () async { + final server = await servePackages(); + server.serve('dev_dep', '1.0.0'); + await dir(appPath, [ + libPubspec( + 'myapp', + '1.2.3', + extras: { + 'workspace': ['a', 'b'], + }, + sdk: '^3.5.0', + ), + dir('a', [ + libPubspec( + 'a', + '1.0.0', + resolutionWorkspace: true, + ), + ]), + dir('b', [ + libPubspec( + 'a', // Has same name as sibling. + '1.0.0', + resolutionWorkspace: true, + ), + ]), + ]).create(); + await pubGet( + environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'}, + error: ''' +Workspace members must have unique names. +`a${s}pubspec.yaml` and `b${s}pubspec.yaml` are both called "a".''', + ); + }); } final s = p.separator;