@@ -35,6 +35,12 @@ class ModuleMigrator extends Migrator {
35
35
final argParser = ArgParser ()
36
36
..addFlag ('built-in-only' ,
37
37
help: 'Migrates global functions without migrating @import.' )
38
+ ..addMultiOption ('safe-at-rule' ,
39
+ help: 'CSS at-rules (without the @) used by postprocessing tools '
40
+ 'that should not be considered to be emitting CSS.' )
41
+ ..addFlag ('unsafe-hoist' ,
42
+ help: 'Allow the migrator to hoist late imports to the top of the '
43
+ 'file even when they emit CSS.' )
38
44
..addMultiOption ('remove-prefix' ,
39
45
abbr: 'p' ,
40
46
help: 'Removes PREFIX from all migrated member names.\n '
@@ -94,14 +100,16 @@ class ModuleMigrator extends Migrator {
94
100
'which prefixed members to forward.' );
95
101
}
96
102
97
- var references = References (importCache, stylesheet, importer);
103
+ var references = References (importCache, stylesheet, importer,
104
+ safeAtRules: argResults! ['safe-at-rule' ] as List <String >);
98
105
var visitor = _ModuleMigrationVisitor (
99
106
importCache, references, globalResults! ['load-path' ] as List <String >,
100
107
migrateDependencies: migrateDependencies,
101
108
builtInOnly: builtInOnly,
102
109
prefixesToRemove: (argResults! ['remove-prefix' ] as List <String >)
103
110
.map ((prefix) => prefix.replaceAll ('_' , '-' )),
104
- forwards: forwards);
111
+ forwards: forwards,
112
+ unsafeHoist: argResults! ['unsafe-hoist' ] as bool );
105
113
var migrated = visitor.run (stylesheet, importer);
106
114
_filesWithRenamedDeclarations.addAll (
107
115
{for (var member in visitor.renamedMembers.keys) member.sourceUrl});
@@ -156,6 +164,11 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
156
164
assertInStylesheet (__builtInUseRules, '_builtInUseRules' );
157
165
Set <String >? __builtInUseRules;
158
166
167
+ /// Set of `@use` rules hoisted from `@import` rules later in the stylesheet.
168
+ Set <String > get _hoistedUseRules =>
169
+ assertInStylesheet (__hoistedUseRules, '_hoistedUseRules' );
170
+ Set <String >? __hoistedUseRules;
171
+
159
172
/// Set of additional `@use` rules for stylesheets at a load path.
160
173
Set <String > get _additionalLoadPathUseRules => assertInStylesheet (
161
174
__additionalLoadPathUseRules, '_additionalLoadPathUseRules' );
@@ -184,6 +197,9 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
184
197
/// from any member visible at the entrypoint.
185
198
var _needsImportOnly = false ;
186
199
200
+ /// The stylesheet currently being migrated.
201
+ late Stylesheet _currentStylesheet;
202
+
187
203
/// Set of variables declared outside the current stylesheet that overrode
188
204
/// `!default` variables within the current stylesheet.
189
205
Set <MemberDeclaration <VariableDeclaration >> get _configuredVariables =>
@@ -211,6 +227,10 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
211
227
/// Whether to migrate only global functions, leaving `@import` rules as-is.
212
228
final bool builtInOnly;
213
229
230
+ /// Whether to allow hoisting imports to the top of the file even when they
231
+ /// emit CSS.
232
+ final bool unsafeHoist;
233
+
214
234
/// Constructs a new module migration visitor.
215
235
///
216
236
/// [importCache] must be the same one used by [references] .
@@ -226,6 +246,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
226
246
super .importCache, this .references, List <String > loadPaths,
227
247
{required super .migrateDependencies,
228
248
required this .builtInOnly,
249
+ required this .unsafeHoist,
229
250
Iterable <String > prefixesToRemove = const [],
230
251
this .forwards = const {}})
231
252
: loadPaths = List .unmodifiable (
@@ -421,10 +442,12 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
421
442
/// visiting it and restores the per-file state afterwards.
422
443
@override
423
444
void visitStylesheet (Stylesheet node) {
445
+ _currentStylesheet = node;
424
446
var oldNamespaces = __namespaces;
425
447
var oldForwardedUrls = __forwardedUrls;
426
448
var oldUsedUrls = __usedUrls;
427
449
var oldBuiltInUseRules = __builtInUseRules;
450
+ var oldHoistedUseRules = __hoistedUseRules;
428
451
var oldLoadPathUseRules = __additionalLoadPathUseRules;
429
452
var oldRelativeUseRules = __additionalRelativeUseRules;
430
453
var oldBeforeFirstImport = _beforeFirstImport;
@@ -438,10 +461,11 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
438
461
? .$2 ??
439
462
rule.url: rule.namespace
440
463
};
464
+ __usedUrls = _namespaces.keys.toSet ();
441
465
_determineNamespaces (node.span.sourceUrl! , _namespaces);
442
- __usedUrls = {};
443
466
__forwardedUrls = {};
444
467
__builtInUseRules = {};
468
+ __hoistedUseRules = {};
445
469
__additionalLoadPathUseRules = {};
446
470
__additionalRelativeUseRules = {};
447
471
_beforeFirstImport = null ;
@@ -452,6 +476,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
452
476
__forwardedUrls = oldForwardedUrls;
453
477
__usedUrls = oldUsedUrls;
454
478
__builtInUseRules = oldBuiltInUseRules;
479
+ __hoistedUseRules = oldHoistedUseRules;
455
480
__additionalLoadPathUseRules = oldLoadPathUseRules;
456
481
__additionalRelativeUseRules = oldRelativeUseRules;
457
482
_beforeFirstImport = oldBeforeFirstImport;
@@ -474,7 +499,8 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
474
499
useRulesToString (_builtInUseRules)),
475
500
beforeExisting: true );
476
501
}
477
- var extras = useRulesToString (_additionalLoadPathUseRules) +
502
+ var extras = useRulesToString (_hoistedUseRules) +
503
+ useRulesToString (_additionalLoadPathUseRules) +
478
504
useRulesToString (_additionalRelativeUseRules) +
479
505
_getAdditionalForwardRules ();
480
506
if (extras == '' ) return ;
@@ -795,7 +821,8 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
795
821
}
796
822
String rulesText;
797
823
798
- var migratedRules = < String > [];
824
+ var inPlaceUseRules = < String > [];
825
+ var loadCssRules = < String > [];
799
826
800
827
var indent = ' ' * node.span.start.column;
801
828
@@ -816,23 +843,35 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
816
843
}
817
844
}
818
845
if (ruleUrl != null ) {
846
+ var canonicalUrl = importCache
847
+ .canonicalize (ruleUrl, baseImporter: importer, baseUrl: currentUrl)!
848
+ .$2;
849
+ var isNested = ! _currentStylesheet.children.contains (node);
819
850
if (builtInOnly) {
820
851
if (migrateDependencies) {
821
852
_upstreamStylesheets.add (currentUrl);
822
853
visitDependency (ruleUrl, import.span);
823
854
_upstreamStylesheets.remove (currentUrl);
824
855
}
825
856
} else if (_useAllowed) {
826
- migratedRules.addAll (_migrateImportToRules (ruleUrl, import.span));
857
+ inPlaceUseRules.addAll (_migrateImportToRules (ruleUrl, import.span));
858
+ } else if (! isNested &&
859
+ (! (references.fileEmitsCss[canonicalUrl] ?? true ) ||
860
+ (unsafeHoist &&
861
+ references.anyMemberReferenced (
862
+ canonicalUrl, currentUrl)))) {
863
+ _hoistedUseRules.addAll (_migrateImportToRules (ruleUrl, import.span));
827
864
} else {
828
- migratedRules.add (_migrateImportToLoadCss (ruleUrl, import.span)
829
- .replaceAll ('\n ' , '\n $indent ' ));
865
+ loadCssRules.add (
866
+ _migrateImportToLoadCss (ruleUrl, import.span, isNested)
867
+ .replaceAll ('\n ' , '\n $indent ' ));
830
868
}
831
869
}
832
870
}
833
871
if (builtInOnly) return ;
834
872
835
- rulesText = migratedRules.join ('$semicolon \n $indent ' );
873
+ rulesText =
874
+ [...inPlaceUseRules, ...loadCssRules].join ('$semicolon \n $indent ' );
836
875
if (rulesText.isEmpty) {
837
876
var span = node.span.extendIfMatches (RegExp (' *$semicolon \n ?' ));
838
877
addPatch (patchDelete (span));
@@ -894,8 +933,9 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
894
933
if ((rules.isEmpty && normalForwardRules == null ) ||
895
934
withClause.isNotEmpty ||
896
935
references.anyMemberReferenced (canonicalUrl, currentUrl)) {
897
- _usedUrls.add (canonicalUrl);
898
- rules.add ('@use $quotedUrl $asClause $withClause ' );
936
+ if (_usedUrls.add (canonicalUrl)) {
937
+ rules.add ('@use $quotedUrl $asClause $withClause ' );
938
+ }
899
939
}
900
940
if (normalForwardRules != null ) rules.addAll (normalForwardRules);
901
941
return rules;
@@ -906,7 +946,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
906
946
///
907
947
/// This is used for migrating `@import` rules that are nested or appear after
908
948
/// some other rules.
909
- String _migrateImportToLoadCss (Uri ruleUrl, FileSpan context) {
949
+ String _migrateImportToLoadCss (Uri ruleUrl, FileSpan context, bool isNested ) {
910
950
var oldUnreferencable = _unreferencable;
911
951
_unreferencable = UnreferencableMembers (_unreferencable);
912
952
for (var declaration in references.allDeclarations) {
@@ -927,7 +967,11 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
927
967
_unreferencable = oldUnreferencable;
928
968
for (var declaration in references.allDeclarations) {
929
969
if (declaration.sourceUrl != canonicalUrl) continue ;
930
- _unreferencable.add (declaration, UnreferencableType .fromNestedImport);
970
+ _unreferencable.add (
971
+ declaration,
972
+ isNested
973
+ ? UnreferencableType .fromNestedImport
974
+ : UnreferencableType .fromLateImport);
931
975
}
932
976
933
977
var meta = _findOrAddBuiltInNamespace ('meta' );
0 commit comments