encode method
Encode the whole package and return a ball Program.
entryFile is the relative path from packageDir to the Dart file that
provides the package's void main() entry point (e.g. 'bin/main.dart').
When omitted, the encoder auto-detects the entry file.
entryFunction is the name of the entry function (default 'main').
scanDirs overrides the set of top-level directories that are scanned for
.dart files. Defaults to ['lib', 'bin'] (plus ['test'] when
includeTests is true).
Implementation
Program encode({String? entryFile, String entryFunction = 'main'}) {
final String resolvedEntry =
entryFile ?? _detectEntryFile() ?? 'bin/main.dart';
final String entryModuleName =
_fileToModule[resolvedEntry] ?? filePathToModuleName(resolvedEntry);
final encoder = DartEncoder();
// Accumulate all base functions across files; build std once at the end.
final userModules = <Module>[];
// External import stubs — deduplicated across all files.
final externalStubs = <String, Module>{};
// Internal module names already known (avoid duplicating in-package stubs).
final inPackageModules = <String>{'std', ..._fileToModule.values};
for (final MapEntry(key: relPath, value: moduleName)
in _fileToModule.entries) {
final file = File(
'${packageDir.path}${Platform.pathSeparator}'
'${relPath.replaceAll('/', Platform.pathSeparator)}',
);
if (!file.existsSync()) continue;
// Prefer the type-RESOLVED unit when [prepareStaticTypes] supplied one
// (issue #488): on it `expression.staticType` is non-null, which is the
// only way the encoder can tell `Set.add` from `List.add`. Without it,
// fall back to the syntax-only `parseString` unit — the historical
// behavior, and still the behavior for every caller that never awaits
// [prepareStaticTypes].
final ast.CompilationUnit unit =
_resolvedUnits[relPath] ??
parseString(
content: file.readAsStringSync(),
throwIfDiagnostics: false,
featureSet: FeatureSet.latestLanguageVersion(),
).unit;
final uriOverrides = _computeUriOverridesFromUnit(relPath, unit);
final (:module, :importStubs) = encoder.encodeModuleFromUnit(
unit,
moduleName: moduleName,
uriToModuleOverrides: uriOverrides,
);
userModules.add(module);
for (final stub in importStubs) {
if (!inPackageModules.contains(stub.name) &&
!externalStubs.containsKey(stub.name)) {
externalStubs[stub.name] = stub;
}
}
}
final (:stdModule, :collectionsModule, :protoModule) = encoder
.buildStdModules();
// Sort user modules so the entry module is last (conventional positioning).
userModules.sort((a, b) {
if (a.name == entryModuleName) return 1;
if (b.name == entryModuleName) return -1;
return a.name.compareTo(b.name);
});
// Collect package manifest files (pubspec.yaml, pubspec.lock, etc.) and
// any other non-Dart resources into a special __assets__ module.
final resourceModule = _collectResources();
return Program()
..name = packageName
..version = packageVersion
..entryModule = entryModuleName
..entryFunction = entryFunction
..modules.addAll([
stdModule,
// coverage:ignore-start
// Empirically confirmed dart:coverage limitation: a null-aware
// spread element (`?expr`) inside a list literal never gets a `DA:`
// hit recorded even when the surrounding call executes and `expr`
// evaluates to a non-null value — verified by running package-level
// tests that reliably hit this line both with a null and a non-null
// `collectionsModule`, and it stayed at 0 hits either way. Real
// coverage (behavior) is proven by the module-presence assertions
// in package_encoder_test.dart.
?collectionsModule,
?protoModule,
// coverage:ignore-end
...externalStubs.values,
...userModules,
?resourceModule,
]);
}