@@ -15,6 +15,7 @@ import '../migration_visitor.dart';
1515import '../migrator.dart' ;
1616import '../patch.dart' ;
1717import '../utils.dart' ;
18+ import '../util/dependency_graph.dart' ;
1819import '../util/member_declaration.dart' ;
1920import '../util/node_modules_importer.dart' ;
2021
@@ -199,6 +200,33 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
199200 /// The values of the --forward flag.
200201 final Set <ForwardType > forwards;
201202
203+ /// Dependencies where keys represent source URIs and values represent imported URIs.
204+ final DependencyGraph _dependencies = DependencyGraph ();
205+
206+ /// Checks for dependency loops between source and imported paths.
207+ ///
208+ /// This method verifies whether importing a path introduces a circular dependency
209+ /// by checking if the imported path is already mapped as a dependency of the source path.
210+ ///
211+ /// Throws a [MigrationException] if a dependency loop is detected.
212+ ///
213+ /// The [source] parameter is the path where the dependency is checked.
214+ /// The [importedPath] parameter is the path being imported.
215+ void _checkDependency (Uri source, Uri importedPath, FileSpan span) {
216+ if (_dependencies.hasDependency (importedPath, source)) {
217+ // Throw an error indicating a potential loop.
218+ var (sourceUrl, _) = _absoluteUrlToDependency (source);
219+ var (importedPathUrl, _) = _absoluteUrlToDependency (importedPath);
220+ throw MigrationSourceSpanException (
221+ 'Dependency loop detected: ${sourceUrl } -> ${importedPathUrl }.\n '
222+ 'To resolve this issue, consider either of the following:\n '
223+ '1. Remove the import statement that causes the dependency loop.\n '
224+ '2. Declare the variables used in the other stylesheet within the same stylesheet.\n '
225+ , span);
226+ }
227+ _dependencies.add (source, importedPath);
228+ }
229+
202230 /// Constructs a new module migration visitor.
203231 ///
204232 /// [importCache] must be the same one used by [references] .
@@ -848,6 +876,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
848876 withClause.isNotEmpty ||
849877 references.anyMemberReferenced (canonicalUrl, currentUrl)) {
850878 _usedUrls.add (canonicalUrl);
879+ _checkDependency (currentUrl, canonicalUrl, context);
851880 rules.add ('@use $quotedUrl $asClause $withClause ' );
852881 }
853882 if (normalForwardRules != null ) rules.addAll (normalForwardRules);
@@ -1223,6 +1252,8 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
12231252 String ? _namespaceForDeclaration (MemberDeclaration declaration) {
12241253 var url = declaration.sourceUrl;
12251254 if (url == currentUrl) return null ;
1255+ // Trace dependencies for loop detection.
1256+ _checkDependency (currentUrl, url, declaration.member.span);
12261257
12271258 // If we can load [declaration] from a library entrypoint URL, do so. Choose
12281259 // the shortest one if there are multiple options.
0 commit comments