exportedDeclarationsOf function

Map<String, PublicDeclaration> exportedDeclarationsOf(
  1. String entryPoint
)

The public declarations entryPoint exports, keyed by name: its own, its parts', and those of every file it re-exports within its package, with show / hide applied.

A package: export of another package is not followed: those names are that package's, re-exported for convenience, and are not this package's to name.

Implementation

Map<String, PublicDeclaration> exportedDeclarationsOf(String entryPoint) {
  final context = _PackageContext.of(entryPoint);
  final visiting = <String>{};

  Map<String, PublicDeclaration> namespaceOf(String file) {
    final path = p.normalize(p.absolute(file));
    final handle = File(path);
    if (!handle.existsSync() || !visiting.add(path)) return const {};
    final source = handle.readAsStringSync();
    final stripped = dartCodeWithoutComments(source);
    final names = <String, PublicDeclaration>{};
    void declareFrom(String declaringFile, String text) {
      for (final d in topLevelDeclarations(text)) {
        if (d.name.startsWith('_')) continue;
        names.putIfAbsent(
          d.name,
          () => PublicDeclaration(d.name, d.kind, declaringFile),
        );
      }
    }

    declareFrom(path, source);
    for (final part in _partDirective.allMatches(stripped)) {
      final partFile = p.normalize(p.join(p.dirname(path), part.group(2)!));
      if (File(partFile).existsSync()) {
        declareFrom(partFile, File(partFile).readAsStringSync());
      }
    }
    for (final export in _exportDirective.allMatches(stripped)) {
      final uri = export.group(2)!;
      String? target;
      final selfPrefix = context == null ? null : 'package:${context.name}/';
      if (selfPrefix != null && uri.startsWith(selfPrefix)) {
        target = p.join(
          context!.libDirectory,
          uri.substring(selfPrefix.length),
        );
      } else if (!uri.contains(':')) {
        target = p.join(p.dirname(path), uri);
      }
      if (target == null) continue;
      var exported = namespaceOf(target);
      final combinators = export.group(3)!;
      for (final c in RegExp(
        r'\b(show|hide)\b([^;]*?)(?=\b(?:show|hide)\b|$)',
      ).allMatches(combinators)) {
        final listed = {for (final n in c.group(2)!.split(',')) n.trim()}
          ..remove('');
        exported = {
          for (final e in exported.entries)
            if (listed.contains(e.key) == (c.group(1) == 'show'))
              e.key: e.value,
        };
      }
      for (final e in exported.entries) {
        names.putIfAbsent(e.key, () => e.value);
      }
    }
    visiting.remove(path);
    return names;
  }

  return namespaceOf(entryPoint);
}