Skip to content

Commit c72bfac

Browse files
committed
fix(dart_frog_cli): workspace package version resolution
1 parent 5ad12e7 commit c72bfac

File tree

8 files changed

+140
-433
lines changed

8 files changed

+140
-433
lines changed

bricks/dart_frog_prod_server/hooks/lib/dart_frog_prod_server_hooks.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ export 'src/dart_pub_get.dart';
55
export 'src/disable_workspace_resolution.dart';
66
export 'src/exit_overrides.dart';
77
export 'src/get_internal_path_dependencies.dart';
8+
export 'src/get_package_config.dart';
89
export 'src/get_pubspec_lock.dart';
10+
export 'src/get_workspace_root.dart';
911
export 'src/uses_workspace_resolution.dart';
1012

1113
/// A void callback function (e.g. `void Function()`).

bricks/dart_frog_prod_server/hooks/lib/src/copy_workspace_pubspec_lock.dart

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,17 @@ import 'dart:io';
22
import 'package:dart_frog_prod_server_hooks/dart_frog_prod_server_hooks.dart';
33
import 'package:mason/mason.dart';
44
import 'package:path/path.dart' as path;
5-
import 'package:yaml/yaml.dart';
65

76
/// Copies the pubspec.lock from the workspace root into the project directory
87
/// in order to ensure the production build uses the exact same versions of all
98
/// dependencies.
109
VoidCallback copyWorkspacePubspecLock(
1110
HookContext context, {
1211
required String projectDirectory,
12+
required String workspaceRoot,
1313
required void Function(int exitCode) exit,
1414
}) {
15-
final workspaceRoot = _getWorkspaceRoot(projectDirectory);
16-
if (workspaceRoot == null) {
17-
context.logger.err(
18-
'Unable to determine workspace root for $projectDirectory',
19-
);
20-
exit(1);
21-
return () {};
22-
}
23-
24-
final pubspecLockFile = File(path.join(workspaceRoot.path, 'pubspec.lock'));
15+
final pubspecLockFile = File(path.join(workspaceRoot, 'pubspec.lock'));
2516
if (!pubspecLockFile.existsSync()) return () {};
2617

2718
try {
@@ -35,45 +26,3 @@ VoidCallback copyWorkspacePubspecLock(
3526
return () {};
3627
}
3728
}
38-
39-
/// Returns the root directory of the nearest Dart workspace.
40-
Directory? _getWorkspaceRoot(String workingDirectory) {
41-
final file = _findNearestAncestor(
42-
where: (path) => _getWorkspaceRootPubspecYaml(cwd: Directory(path)),
43-
cwd: Directory(workingDirectory),
44-
);
45-
if (file == null || !file.existsSync()) return null;
46-
return Directory(path.dirname(file.path));
47-
}
48-
49-
/// The workspace root `pubspec.yaml` file for this project.
50-
File? _getWorkspaceRootPubspecYaml({required Directory cwd}) {
51-
try {
52-
final pubspecYamlFile = File(path.join(cwd.path, 'pubspec.yaml'));
53-
if (!pubspecYamlFile.existsSync()) return null;
54-
final pubspec = loadYaml(pubspecYamlFile.readAsStringSync());
55-
if (pubspec is! YamlMap) return null;
56-
final workspace = pubspec['workspace'] as List?;
57-
if (workspace?.isEmpty ?? true) return null;
58-
return pubspecYamlFile;
59-
} on Exception {
60-
return null;
61-
}
62-
}
63-
64-
/// Finds nearest ancestor file
65-
/// relative to the [cwd] that satisfies [where].
66-
File? _findNearestAncestor({
67-
required File? Function(String path) where,
68-
required Directory cwd,
69-
}) {
70-
Directory? prev;
71-
var dir = cwd;
72-
while (prev?.path != dir.path) {
73-
final file = where(dir.path);
74-
if (file?.existsSync() ?? false) return file;
75-
prev = dir;
76-
dir = dir.parent;
77-
}
78-
return null;
79-
}

bricks/dart_frog_prod_server/hooks/lib/src/disable_workspace_resolution.dart

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,60 @@
11
import 'dart:io';
22
import 'package:dart_frog_prod_server_hooks/dart_frog_prod_server_hooks.dart';
33
import 'package:mason/mason.dart';
4+
import 'package:package_config/package_config_types.dart';
45
import 'package:path/path.dart' as path;
6+
import 'package:pubspec_parse/pubspec_parse.dart';
57
import 'package:yaml/yaml.dart';
68
import 'package:yaml_edit/yaml_edit.dart';
79

810
/// Opts out of dart workspaces until we can generate per package lockfiles.
911
/// https://github.com/dart-lang/pub/issues/4594
1012
VoidCallback disableWorkspaceResolution(
1113
HookContext context, {
14+
required PackageConfig packageConfig,
1215
required String projectDirectory,
16+
required String workspaceRoot,
1317
required void Function(int exitCode) exit,
1418
}) {
19+
final VoidCallback restoreWorkspaceResolution;
1520
try {
16-
return overrideResolutionInPubspecOverrides(projectDirectory);
21+
restoreWorkspaceResolution = overrideResolutionInPubspecOverrides(
22+
projectDirectory,
23+
);
1724
} on Exception catch (e) {
1825
context.logger.err('$e');
1926
exit(1);
2027
return () {}; // no-op
2128
}
29+
30+
// Get all package dependencies.
31+
final pubspecFile = File(path.join(projectDirectory, 'pubspec.yaml'));
32+
final pubspec = Pubspec.parse(pubspecFile.readAsStringSync());
33+
final allDependencies = [
34+
...pubspec.dependencies.keys,
35+
...pubspec.devDependencies.keys,
36+
];
37+
38+
// Find path dependencies based on the package_config.json.
39+
final pathDependencies = packageConfig.packages.where(
40+
(package) => package.relativeRoot && allDependencies.contains(package.name),
41+
);
42+
43+
// Add dependency_overrides to the pubspec_overrides.yaml.
44+
final pubspecOverridesFile = File(
45+
path.join(projectDirectory, 'pubspec_overrides.yaml'),
46+
);
47+
final contents = pubspecOverridesFile.readAsStringSync();
48+
final editor = YamlEditor(contents)..update(['dependency_overrides'], {});
49+
for (final package in pathDependencies) {
50+
editor.update(
51+
['dependency_overrides', package.name],
52+
{'path': path.relative(package.root.path, from: projectDirectory)},
53+
);
54+
}
55+
pubspecOverridesFile.writeAsStringSync(editor.toString());
56+
57+
return restoreWorkspaceResolution;
2258
}
2359

2460
VoidCallback overrideResolutionInPubspecOverrides(String projectDirectory) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import 'dart:io';
2+
3+
import 'package:package_config/package_config_types.dart';
4+
import 'package:path/path.dart' as path;
5+
6+
PackageConfig? getPackageConfig(
7+
String workspaceRoot, {
8+
path.Context? pathContext,
9+
}) {
10+
final pathResolver = pathContext ?? path.context;
11+
final packageConfigFile = File(
12+
pathResolver.join(workspaceRoot, '.dart_tool/package_config.json'),
13+
);
14+
if (!packageConfigFile.existsSync()) return null;
15+
16+
try {
17+
final content = packageConfigFile.readAsStringSync();
18+
return PackageConfig.parseString(content, packageConfigFile.uri);
19+
} on Exception {
20+
return null;
21+
}
22+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'dart:io';
2+
3+
import 'package:path/path.dart' as path;
4+
import 'package:yaml/yaml.dart';
5+
6+
/// Returns the root directory of the nearest Dart workspace.
7+
Directory? getWorkspaceRoot(String workingDirectory) {
8+
final file = _findNearestAncestor(
9+
where: (path) => _getWorkspaceRootPubspecYaml(cwd: Directory(path)),
10+
cwd: Directory(workingDirectory),
11+
);
12+
if (file == null || !file.existsSync()) return null;
13+
return Directory(path.dirname(file.path));
14+
}
15+
16+
/// The workspace root `pubspec.yaml` file for this project.
17+
File? _getWorkspaceRootPubspecYaml({required Directory cwd}) {
18+
try {
19+
final pubspecYamlFile = File(path.join(cwd.path, 'pubspec.yaml'));
20+
if (!pubspecYamlFile.existsSync()) return null;
21+
final pubspec = loadYaml(pubspecYamlFile.readAsStringSync());
22+
if (pubspec is! YamlMap) return null;
23+
final workspace = pubspec['workspace'] as List?;
24+
if (workspace?.isEmpty ?? true) return null;
25+
return pubspecYamlFile;
26+
} on Exception {
27+
return null;
28+
}
29+
}
30+
31+
/// Finds nearest ancestor file
32+
/// relative to the [cwd] that satisfies [where].
33+
File? _findNearestAncestor({
34+
required File? Function(String path) where,
35+
required Directory cwd,
36+
}) {
37+
Directory? prev;
38+
var dir = cwd;
39+
while (prev?.path != dir.path) {
40+
final file = where(dir.path);
41+
if (file?.existsSync() ?? false) return file;
42+
prev = dir;
43+
dir = dir.parent;
44+
}
45+
return null;
46+
}

bricks/dart_frog_prod_server/hooks/pre_gen.dart

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,50 @@ Future<void> preGen(
3333
VoidCallback? revertPubspecLock;
3434

3535
if (usesWorkspaces) {
36+
final workspaceRoot = getWorkspaceRoot(projectDirectory.path);
37+
if (workspaceRoot == null) {
38+
context.logger.err(
39+
'Unable to determine workspace root for $projectDirectory',
40+
);
41+
return exit(1);
42+
}
43+
44+
// We need to make sure the package_config.json is up to date.
45+
await dartPubGet(
46+
context,
47+
workingDirectory: projectDirectory.path,
48+
runProcess: runProcess,
49+
exit: exit,
50+
);
51+
52+
final packageConfig = getPackageConfig(workspaceRoot.path);
53+
if (packageConfig == null) {
54+
context.logger.err(
55+
'Unable to find package_config.json for ${workspaceRoot.path}',
56+
);
57+
return exit(1);
58+
}
59+
3660
// Disable workspace resolution until we can generate per-package lockfiles.
3761
// https://github.com/dart-lang/pub/issues/4594
3862
restoreWorkspaceResolution = disableWorkspaceResolution(
3963
context,
64+
packageConfig: packageConfig,
4065
projectDirectory: projectDirectory.path,
66+
workspaceRoot: workspaceRoot.path,
4167
exit: exit,
4268
);
4369
// Copy the pubspec.lock from the workspace root to ensure the same versions
4470
// of dependencies are used in the production build.
4571
revertPubspecLock = copyWorkspacePubspecLock(
4672
context,
4773
projectDirectory: projectDirectory.path,
74+
workspaceRoot: workspaceRoot.path,
4875
exit: exit,
4976
);
5077
}
5178

52-
// We need to make sure that the pubspec.lock file is up to date
79+
// We need to make sure that the pubspec.lock file is up to date.
5380
await dartPubGet(
5481
context,
5582
workingDirectory: projectDirectory.path,

bricks/dart_frog_prod_server/hooks/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ dependencies:
99
equatable: ^2.0.5
1010
io: ^1.0.3
1111
mason: ^0.1.0
12+
package_config: ^2.2.0
1213
path: ^1.8.1
14+
pubspec_parse: ^1.5.0
1315
yaml: ^3.1.2
1416
yaml_edit: ^2.2.2
1517

0 commit comments

Comments
 (0)