@@ -24,25 +24,13 @@ class ModuleMigrator extends Migrator {
2424 final name = "module" ;
2525 final description = "Migrates stylesheets to the new module system." ;
2626
27- /// Global variables defined at any time during the migrator run.
28- final _variables = normalizedMap <List <VariableDeclaration >>();
29-
30- /// Global mixins defined at any time during the migrator run.
31- final _mixins = normalizedMap <MixinRule >();
32-
33- /// Global functions defined at any time during the migrator run.
34- final _functions = normalizedMap <FunctionRule >();
35-
36- /// Global variables declared with !default that could be configured.
37- ///final _configurableVariables = normalizedMap<VariableDeclaration>();
38-
3927 /// Runs the module migrator on [entrypoint] and its dependencies and returns
4028 /// a map of migrated contents.
4129 ///
4230 /// If [migrateDependencies] is false, the migrator will still be run on
4331 /// dependencies, but they will be excluded from the resulting map.
4432 Map <Uri , String > migrateFile (Uri entrypoint) {
45- var migrated = _ModuleMigrationVisitor (this ).run (entrypoint);
33+ var migrated = _ModuleMigrationVisitor ().run (entrypoint);
4634 if (! migrateDependencies) {
4735 migrated.removeWhere ((url, contents) => url != entrypoint);
4836 }
@@ -51,7 +39,17 @@ class ModuleMigrator extends Migrator {
5139}
5240
5341class _ModuleMigrationVisitor extends MigrationVisitor {
54- final ModuleMigrator migrator;
42+ /// Global variables defined at any time during the migrator run.
43+ ///
44+ /// We store all declarations instead of just the most recent one for use in
45+ /// detecting configurable variables.
46+ final _globalVariables = normalizedMap <List <VariableDeclaration >>();
47+
48+ /// Global mixins defined at any time during the migrator run.
49+ final _globalMixins = normalizedMap <MixinRule >();
50+
51+ /// Global functions defined at any time during the migrator run.
52+ final _globalFunctions = normalizedMap <FunctionRule >();
5553
5654 /// Namespaces of modules used in this stylesheet.
5755 Map <Uri , String > _namespaces = {};
@@ -79,7 +77,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
7977 /// Note: We always set [migratedDependencies] to true since the module
8078 /// migrator needs to always run on dependencies. The `migrateFile` method of
8179 /// the module migrator will filter out the dependencies' migration results.
82- _ModuleMigrationVisitor (this .migrator ) : super (migrateDependencies: true );
80+ _ModuleMigrationVisitor () : super (migrateDependencies: true );
8381
8482 /// Returns the migrated contents of this stylesheet, based on [patches] and
8583 /// [_additionalUseRules] , or null if the stylesheet does not change.
@@ -127,7 +125,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
127125 @override
128126 void visitFunctionExpression (FunctionExpression node) {
129127 visitInterpolation (node.name);
130- patchNamespaceForFunction (node.name.asPlain, (name, namespace) {
128+ _patchNamespaceForFunction (node.name.asPlain, (name, namespace) {
131129 addPatch (Patch (node.name.span, "$namespace .$name " ));
132130 });
133131 visitArgumentInvocation (node.arguments);
@@ -142,7 +140,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
142140 return ;
143141 }
144142 var fnName = nameArgument as StringExpression ;
145- patchNamespaceForFunction (fnName.text.asPlain, (name, namespace) {
143+ _patchNamespaceForFunction (fnName.text.asPlain, (name, namespace) {
146144 var span = fnName.span;
147145 if (fnName.hasQuotes) {
148146 span = span.file.span (span.start.offset + 1 , span.end.offset - 1 );
@@ -162,13 +160,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
162160 /// when namespaced, and the namespace itself. The name will match the name
163161 /// provided to the outer function except for built-in functions whose name
164162 /// within a module differs from its original name.
165- void patchNamespaceForFunction (
163+ void _patchNamespaceForFunction (
166164 String name, void patcher (String name, String namespace)) {
167165 if (name == null ) return ;
168166 if (_localScope? .isLocalFunction (name) ?? false ) return ;
169167
170- var namespace = migrator._functions .containsKey (name)
171- ? namespaceForNode (migrator._functions [name])
168+ var namespace = _globalFunctions .containsKey (name)
169+ ? _namespaceForNode (_globalFunctions [name])
172170 : null ;
173171
174172 if (namespace == null ) {
@@ -183,7 +181,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
183181 /// Declares the function within the current scope before visiting it.
184182 @override
185183 void visitFunctionRule (FunctionRule node) {
186- declareFunction (node);
184+ _declareFunction (node);
187185 super .visitFunctionRule (node);
188186 }
189187
@@ -209,19 +207,33 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
209207 visitDependency (Uri .parse (import.url), _currentUrl);
210208 _namespaces[_lastUrl] = namespaceForPath (import.url);
211209
212- var overrides = [];
213- for (var name in migrator._variables.keys) {
214- var declarations = migrator._variables[name];
210+ var configured = _findConfiguredVariables (node, import);
211+ var config = "" ;
212+ if (configured.isNotEmpty) {
213+ config = " with (\n " + configured.join (',\n ' ) + "\n )" ;
214+ }
215+ addPatch (Patch (node.span, '@use ${import .span .text }$config ' ));
216+ }
217+
218+ /// Finds all configured variables from the given import.
219+ ///
220+ /// This returns a list of "$var: <expression>" strings that can be used to
221+ /// construct the configuration for a use rule.
222+ ///
223+ /// If a variable was configured in a downstream stylesheet, this will instead
224+ /// add a forward rule to make it accessible.
225+ List <String > _findConfiguredVariables (ImportRule node, Import import) {
226+ var configured = < String > [];
227+ for (var name in _globalVariables.keys) {
228+ var declarations = _globalVariables[name];
215229 VariableDeclaration lastNonDefault = declarations[0 ];
216230 for (var i = 1 ; i < declarations.length; i++ ) {
217231 var declaration = declarations[i];
218232 if (! declaration.isGuarded) {
219233 lastNonDefault = declaration;
220234 } else if (declaration.span.sourceUrl == _lastUrl) {
221235 if (lastNonDefault.span.sourceUrl == _currentUrl) {
222- // This configurable variable was set within the current stylesheet,
223- // so add to overrides.
224- overrides.add ("\$ $name : ${lastNonDefault .expression }" );
236+ configured.add ("\$ $name : ${lastNonDefault .expression }" );
225237 } else if (lastNonDefault.span.sourceUrl != _lastUrl) {
226238 // A downstream stylesheet configures this variable, so forward it.
227239 var semicolon = _currentUrl.path.endsWith ('.sass' ) ? '' : ';' ;
@@ -235,20 +247,16 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
235247 }
236248 }
237249 }
238- var config = "" ;
239- if (overrides.isNotEmpty) {
240- config = " with (\n " + overrides.join (',\n ' ) + "\n )" ;
241- }
242- addPatch (Patch (node.span, '@use ${import .span .text }$config ' ));
250+ return configured;
243251 }
244252
245253 /// Adds a namespace to any mixin include that requires it.
246254 @override
247255 void visitIncludeRule (IncludeRule node) {
248256 super .visitIncludeRule (node);
249257 if (_localScope? .isLocalMixin (node.name) ?? false ) return ;
250- if (! migrator._mixins .containsKey (node.name)) return ;
251- var namespace = namespaceForNode (migrator._mixins [node.name]);
258+ if (! _globalMixins .containsKey (node.name)) return ;
259+ var namespace = _namespaceForNode (_globalMixins [node.name]);
252260 if (namespace == null ) return ;
253261 var endName = node.arguments.span.start.offset;
254262 var startName = endName - node.name.length;
@@ -259,7 +267,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
259267 /// Declares the mixin within the current scope before visiting it.
260268 @override
261269 void visitMixinRule (MixinRule node) {
262- declareMixin (node);
270+ _declareMixin (node);
263271 super .visitMixinRule (node);
264272 }
265273
@@ -276,63 +284,59 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
276284 if (_localScope? .isLocalVariable (node.name) ?? false ) {
277285 return ;
278286 }
279- if (! migrator._variables.containsKey (node.name)) return ;
280- var lastRealDeclaration = migrator._variables[node.name]
287+ if (! _globalVariables.containsKey (node.name)) return ;
288+
289+ /// Declarations where the expression is null are fake ones used to track
290+ /// configured variables within indirect dependencies.
291+ /// See [_findConfiguredVariables] .
292+ var lastRealDeclaration = _globalVariables[node.name]
281293 .lastWhere ((node) => node.expression != null );
282- var namespace = namespaceForNode (lastRealDeclaration);
294+ var namespace = _namespaceForNode (lastRealDeclaration);
283295 if (namespace == null ) return ;
284296 addPatch (Patch (node.span, "\$ $namespace .${node .name }" ));
285297 }
286298
287299 /// Declares a variable within the current scope before visiting it.
288300 @override
289301 void visitVariableDeclaration (VariableDeclaration node) {
290- declareVariable (node);
302+ _declareVariable (node);
291303 super .visitVariableDeclaration (node);
292304 }
293305
294306 /// Declares a variable within this stylesheet, in the current local scope if
295307 /// it exists, or as a global variable otherwise.
296- void declareVariable (VariableDeclaration node) {
308+ void _declareVariable (VariableDeclaration node) {
297309 if (_localScope == null || node.isGlobal) {
298- /*if (node.isGuarded) {
299- migrator._configurableVariables[node.name] = node;
300-
301- // Don't override if variable already exists.
302- migrator._variables.putIfAbsent(node.name, () => node);
303- } else {
304- migrator._variables[node.name] = node;
305- }*/
306- migrator._variables.putIfAbsent (node.name, () => []);
307- migrator._variables[node.name].add (node);
310+ _globalVariables.putIfAbsent (node.name, () => []);
311+ _globalVariables[node.name].add (node);
308312 } else {
309313 _localScope.variables.add (node.name);
310314 }
311315 }
312316
313317 /// Declares a mixin within this stylesheet, in the current local scope if
314318 /// it exists, or as a global mixin otherwise.
315- void declareMixin (MixinRule node) {
319+ void _declareMixin (MixinRule node) {
316320 if (_localScope == null ) {
317- migrator._mixins [node.name] = node;
321+ _globalMixins [node.name] = node;
318322 } else {
319323 _localScope.mixins.add (node.name);
320324 }
321325 }
322326
323327 /// Declares a function within this stylesheet, in the current local scope if
324328 /// it exists, or as a global function otherwise.
325- void declareFunction (FunctionRule node) {
329+ void _declareFunction (FunctionRule node) {
326330 if (_localScope == null ) {
327- migrator._functions [node.name] = node;
331+ _globalFunctions [node.name] = node;
328332 } else {
329333 _localScope.functions.add (node.name);
330334 }
331335 }
332336
333337 /// Finds the namespace for the stylesheet containing [node] , adding a new use
334338 /// rule if necessary.
335- String namespaceForNode (SassNode node) {
339+ String _namespaceForNode (SassNode node) {
336340 if (node.span.sourceUrl == _currentUrl) return null ;
337341 if (! _namespaces.containsKey (node.span.sourceUrl)) {
338342 /// Add new use rule for indirect dependency
0 commit comments