@@ -15,6 +15,7 @@ import '../migration_visitor.dart';
15
15
import '../migrator.dart' ;
16
16
import '../patch.dart' ;
17
17
import '../utils.dart' ;
18
+ import '../util/dependency_graph.dart' ;
18
19
import '../util/member_declaration.dart' ;
19
20
import '../util/node_modules_importer.dart' ;
20
21
@@ -199,6 +200,33 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
199
200
/// The values of the --forward flag.
200
201
final Set <ForwardType > forwards;
201
202
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
+
202
230
/// Constructs a new module migration visitor.
203
231
///
204
232
/// [importCache] must be the same one used by [references] .
@@ -848,6 +876,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
848
876
withClause.isNotEmpty ||
849
877
references.anyMemberReferenced (canonicalUrl, currentUrl)) {
850
878
_usedUrls.add (canonicalUrl);
879
+ _checkDependency (currentUrl, canonicalUrl, context);
851
880
rules.add ('@use $quotedUrl $asClause $withClause ' );
852
881
}
853
882
if (normalForwardRules != null ) rules.addAll (normalForwardRules);
@@ -1223,6 +1252,8 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
1223
1252
String ? _namespaceForDeclaration (MemberDeclaration declaration) {
1224
1253
var url = declaration.sourceUrl;
1225
1254
if (url == currentUrl) return null ;
1255
+ // Trace dependencies for loop detection.
1256
+ _checkDependency (currentUrl, url, declaration.member.span);
1226
1257
1227
1258
// If we can load [declaration] from a library entrypoint URL, do so. Choose
1228
1259
// the shortest one if there are multiple options.
0 commit comments