visitImportDirective method
Visit a SImportDirective.
Implementation
@override
Object? visitImportDirective(SImportDirective node) {
final importUriString = (node.uri as SSimpleStringLiteral?)?.value;
if (importUriString == null) {
Logger.warn(
"[visitImportDirective] Import directive with null URI string.",
);
return null; // Return null if the URI is null
}
Logger.debug(
"[InterpreterVisitor.visitImportDirective] START processing import: $importUriString",
);
Uri resolvedUri;
final importUri = Uri.parse(importUriString);
if (importUri.hasScheme) {
// DFUB2 — any already-absolute URI (dart:, package:, file:, …) is
// self-resolving and needs no base. Resolving an absolute URI against a
// base returns the absolute URI unchanged, so this only widens the set of
// imports that succeed WITHOUT a base (notably absolute `file:` imports),
// matching upstream. The downstream module loader decides whether such a
// URI is actually loadable.
resolvedUri = importUri;
Logger.debug(
"[visitImportDirective] Using absolute/unresolvable URI: $resolvedUri",
);
} else {
final baseUri = moduleContext.currentLibrary;
if (baseUri != null) {
Logger.debug(
"[visitImportDirective] Attempting to resolve relative URI '$importUriString' relative to '$baseUri'",
);
resolvedUri = baseUri.resolveUri(importUri);
Logger.debug(
"[visitImportDirective] Resolved relative URI: $resolvedUri",
);
} else {
throw RuntimeD4rtException(
"Unable to resolve relative import '$importUriString': Base URI not defined in ModuleLoader. "
"Either provide a basePath parameter or use absolute URIs.",
);
}
}
final prefixIdentifier = node.prefix; // Get the prefix identifier
final prefixName = prefixIdentifier?.name;
// Extract the show/hide combinators from the import directive BEFORE loading
Set<String>? showNames;
Set<String>? hideNames;
for (final combinator in node.combinators) {
if (combinator is SShowCombinator) {
showNames ??= {};
showNames.addAll(combinator.shownNames.map((id) => id.name));
Logger.debug(
"[visitImportDirective] Combinator: show ${combinator.shownNames.map((id) => id.name).join(', ')}",
);
} else if (combinator is SHideCombinator) {
hideNames ??= {};
hideNames.addAll(combinator.hiddenNames.map((id) => id.name));
Logger.debug(
"[visitImportDirective] Combinator: hide ${combinator.hiddenNames.map((id) => id.name).join(', ')}",
);
}
}
Logger.debug(
"[visitImportDirective] Loading module for resolved URI: $resolvedUri (prefix: $prefixName, show: $showNames, hide: $hideNames)",
);
// DFUB13 — this is the TOP-LEVEL directive: the script the user ran, which
// has no enclosing module to supply owner context, so it is attached here.
// The owner MUST be read before the call: loadModule advances
// `currentLibrary` to the module it is loading, so reading it in the catch
// would report owner == target.
//
// It is null for a bare `source:` string, and there the wrap is SKIPPED
// rather than filled with a placeholder. Owner context earns its keep by
// distinguishing the file holding the directive from the file that could
// not be found; with a single anonymous script those coincide, so naming a
// synthetic owner would restate the target and leak an internal URI.
final ownerUri = moduleContext.currentLibrary;
// Pass show/hide to loadModule so bridged content respects the filters
LoadedModule loadedModule;
try {
loadedModule = moduleContext.loadModule(
resolvedUri,
showNames: showNames,
hideNames: hideNames,
);
} on D4rtException catch (e) {
if (ownerUri == null) rethrow;
throw wrapDirectiveError('import', ownerUri, resolvedUri, e);
}
if (prefixName != null) {
Logger.debug(
"[visitImportDirective] Importation du module '${resolvedUri.toString()}' avec le préfixe '$prefixName'. Show: $showNames, Hide: $hideNames",
);
Environment envForPrefix;
if (showNames != null || hideNames != null) {
// Apply show/hide to the exported environment of the loaded module BEFORE defining it for the prefix
envForPrefix = loadedModule.exportedEnvironment.shallowCopyFiltered(
showNames: showNames,
hideNames: hideNames,
);
} else {
envForPrefix = loadedModule.exportedEnvironment;
}
environment.definePrefixedImport(prefixName, envForPrefix);
} else {
Logger.debug(
"[visitImportDirective] Direct import of module '${resolvedUri.toString()}' into the current environment. Show: $showNames, Hide: $hideNames",
);
// Apply show/hide directly during import into the current environment
environment.importEnvironment(
loadedModule.exportedEnvironment,
show: showNames,
hide: hideNames,
);
}
return null; // Import directives do not produce a value.
}