Skip to content

Commit

Permalink
Allow calling findExtensions with packageDir
Browse files Browse the repository at this point in the history
  • Loading branch information
sigurdm committed Sep 27, 2024
1 parent e1d8dbe commit 3de2d4e
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 15 deletions.
57 changes: 43 additions & 14 deletions pkgs/extension_discovery/lib/extension_discovery.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ final class Extension {
/// ## Locating `.dart_tool/package_config.json`
///
/// This method requires the location of the [packageConfig], unless the current
/// isolate has been setup for package resolution.
/// Notably, Dart programs compiled for AOT cannot find their own
/// `package_config.json`.
/// isolate has been setup for package resolution. Notably, Dart programs
/// compiled for AOT cannot find their own `package_config.json`.
///
/// Alternatively a [packageRootDir] can be provided. That is a file uri the the
/// directory containing the `pubspec.yaml` of [targetPackage]. In that case the
/// package_config will be searched by looking in all parent directories.
///
/// It is an error to specify both [packageRootDir] and [packageConfig].
///
/// If operating on a project that isn't the current project, for example, if
/// you are developing a tool that users are globally activating and then
Expand All @@ -100,17 +105,15 @@ final class Extension {
///
/// ### Caching results
///
/// When [useCache] is `true` then the detected extensions will be cached
/// in `.dart_tool/extension_discovery/<targetPackage>.yaml`.
/// This function will compare modification timestamps of
/// `.dart_tool/package_config.json` with the cache file, before reusing cached
/// results.
/// This function will also treat relative path-dependencies as mutable
/// packages, and check such packages for extensions every time [findExtensions]
/// is called. Notably, it'll compare the modification time of the
/// `extension/<targetPackage>/config.yaml` file, to ensure that it's older than
/// the extension cache file. Otherwise, it'll reload the extension
/// configuration.
/// When [useCache] is `true` then the detected extensions will be cached in
/// `.dart_tool/extension_discovery/<targetPackage>.yaml`. This function will
/// compare modification timestamps of `.dart_tool/package_config.json` with the
/// cache file, before reusing cached results. This function will also treat
/// relative path-dependencies as mutable packages, and check such packages for
/// extensions every time [findExtensions] is called. Notably, it'll compare the
/// modification time of the `extension/<targetPackage>/config.yaml` file, to
/// ensure that it's older than the extension cache file. Otherwise, it'll
/// reload the extension configuration.
///
/// ## Exceptions
///
Expand All @@ -128,8 +131,18 @@ Future<List<Extension>> findExtensions(
String targetPackage, {
bool useCache = true,
Uri? packageConfig,
Uri? packageRootDir,
}) async {
if (packageRootDir != null) {
if (packageConfig != null) {
throw ArgumentError(
'Specify only one of `packageConfig` and `packageRootDir`.',
);
}
packageConfig = _findPackageConfig(packageRootDir);
}
packageConfig ??= await Isolate.packageConfig;

if (packageConfig == null) {
throw UnsupportedError(
'packageConfigUri must be provided, if not running in JIT mode',
Expand All @@ -156,6 +169,22 @@ Future<List<Extension>> findExtensions(
);
}

Uri? _findPackageConfig(Uri packageDir) {
if (!packageDir.path.endsWith('/')) {
packageDir = packageDir.replace(path: '${packageDir.path}/');
}
while (true) {
final packageConfigCandidate =
packageDir.resolve('.dart_tool/package_config.json');
if (File.fromUri(packageConfigCandidate).existsSync()) {
return packageConfigCandidate;
}
final next = packageDir.resolve('..');
if (next == packageDir) return null;
packageDir = next;
}
}

/// Find extensions with normalized arguments.
Future<List<Extension>> _findExtensions({
required String targetPackage,
Expand Down
56 changes: 56 additions & 0 deletions pkgs/extension_discovery/test/find_extensions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,60 @@ writtenAsYaml: true
);
}
});

test('finds extensions in a workspace with `packageDir`', () async {
final pkgLibDir = await Isolate.resolvePackageUri(
Uri.parse('package:extension_discovery/'),
);
final pkgDir = pkgLibDir!.resolve('..');

await d.dir('workspace', [
d.pubspec({
'name': '_',
'environment': {'sdk': '^3.5.0'},
'workspace': ['myapp/'],
}),
d.dir('myapp', [
d.pubspec({
'name': 'myapp',
'dependencies': {
'extension_discovery': {'path': pkgDir.toFilePath()},
'foo': {'path': '../../foo'},
},
'environment': {'sdk': '^3.5.0'},
'resolution': 'workspace'
})
])
]).create();

await d.dir('foo', [
d.pubspec({
'name': 'foo',
'environment': {'sdk': '^3.0.0'},
}),
// It has a config.yaml for myapp
d.dir('extension/myapp', [
d.json('config.yaml', {'fromFoo': true}),
]),
]).create();

// Get dependencies
await d.dartPubGet(d.path('workspace'));
final [extension] = await findExtensions(
'myapp',
packageRootDir: d.fileUri('workspace/myapp'),
);
expect(extension.config, {'fromFoo': true});
expect(extension.package, 'foo');
expect(extension.rootUri, d.fileUri('foo/'));
expect(extension.packageUri, Uri.parse('lib/'));

// Check that there is a myapp.json cache file
await d
.file(
'workspace/.dart_tool/extension_discovery/myapp.json',
isNotEmpty,
)
.validate();
});
}
2 changes: 1 addition & 1 deletion pkgs/extension_discovery/test/test_descriptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ Future<String> dart([
}

Future<String> dartPubGet(String folder) async =>
await dart('pub', 'get', '-C', d.path('myapp'));
await dart('pub', 'get', '-C', folder);

0 comments on commit 3de2d4e

Please sign in to comment.