Skip to content

Commit c5f8ac8

Browse files
authored
Add --built-in-only flag to module migrator (#259)
Also fixes some built-in functions that weren't being migrated.
1 parent c27792b commit c5f8ac8

23 files changed

+340
-42
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
## 2.2.0
2+
3+
### Module Migrator
4+
5+
* Add a new `--built-in-only` flag, which migrates global functions to their
6+
`sass:` module equivalents, while leaving `@import` rules unchanged.
7+
8+
* Fix bug where some functions (`opacity`, `is-bracketed`, and
9+
`selector-extend`) would not be migrated.
10+
111
## 2.1.0
212

313
### Color Function Migrator

lib/src/migration_visitor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ abstract class MigrationVisitor extends ScopedAstVisitor {
6666
/// syntax, in which case this returns an empty string.
6767
String get semicolon => currentUrl.path.endsWith('.sass') ? "" : ";";
6868

69-
MigrationVisitor(this.importCache, this.migrateDependencies);
69+
MigrationVisitor(this.importCache, {required this.migrateDependencies});
7070

7171
/// Runs a new migration on [stylesheet] (and its dependencies, if
7272
/// [migrateDependencies] is true) and returns a map of migrated contents.

lib/src/migrators/calc_interpolation.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,17 @@ class CalculationInterpolationMigrator extends Migrator {
1919
@override
2020
Map<Uri, String> migrateFile(
2121
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
22-
var visitor =
23-
_CalculationInterpolationVisitor(importCache, migrateDependencies);
22+
var visitor = _CalculationInterpolationVisitor(importCache,
23+
migrateDependencies: migrateDependencies);
2424
var result = visitor.run(stylesheet, importer);
2525
missingDependencies.addAll(visitor.missingDependencies);
2626
return result;
2727
}
2828
}
2929

3030
class _CalculationInterpolationVisitor extends MigrationVisitor {
31-
_CalculationInterpolationVisitor(
32-
ImportCache importCache, bool migrateDependencies)
33-
: super(importCache, migrateDependencies);
31+
_CalculationInterpolationVisitor(super.importCache,
32+
{required super.migrateDependencies});
3433

3534
@override
3635
void visitFunctionExpression(FunctionExpression node) {

lib/src/migrators/color.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class ColorMigrator extends Migrator {
2323
Map<Uri, String> migrateFile(
2424
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
2525
var references = References(importCache, stylesheet, importer);
26-
var visitor =
27-
_ColorMigrationVisitor(references, importCache, migrateDependencies);
26+
var visitor = _ColorMigrationVisitor(references, importCache,
27+
migrateDependencies: migrateDependencies);
2828
var result = visitor.run(stylesheet, importer);
2929
missingDependencies.addAll(visitor.missingDependencies);
3030
return result;
@@ -37,8 +37,8 @@ final _colorUrl = Uri(scheme: 'sass', path: 'color');
3737
class _ColorMigrationVisitor extends MigrationVisitor {
3838
final References references;
3939

40-
_ColorMigrationVisitor(
41-
this.references, super.importCache, super.migrateDependencies);
40+
_ColorMigrationVisitor(this.references, super.importCache,
41+
{required super.migrateDependencies});
4242

4343
/// The namespace of an existing `@use "sass:color"` rule in the current
4444
/// file, if any.

lib/src/migrators/division.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ More info: https://sass-lang.com/d/slash-div""";
6666
Map<Uri, String> migrateFile(
6767
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
6868
var visitor = _DivisionMigrationVisitor(
69-
importCache, isPessimistic, useMultiplication, migrateDependencies);
69+
importCache, isPessimistic, useMultiplication,
70+
migrateDependencies: migrateDependencies);
7071
var result = visitor.run(stylesheet, importer);
7172
missingDependencies.addAll(visitor.missingDependencies);
7273
return result;
@@ -80,9 +81,9 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
8081
final bool isPessimistic;
8182
final bool useMultiplication;
8283

83-
_DivisionMigrationVisitor(ImportCache importCache, this.isPessimistic,
84-
this.useMultiplication, bool migrateDependencies)
85-
: super(importCache, migrateDependencies);
84+
_DivisionMigrationVisitor(
85+
super.importCache, this.isPessimistic, this.useMultiplication,
86+
{required super.migrateDependencies});
8687

8788
/// True when division is allowed by the context the current node is in.
8889
var _isDivisionAllowed = false;

lib/src/migrators/module.dart

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class ModuleMigrator extends Migrator {
3232

3333
@override
3434
final argParser = ArgParser()
35+
..addFlag('built-in-only',
36+
help: 'Migrates global functions without migrating @import.')
3537
..addMultiOption('remove-prefix',
3638
abbr: 'p',
3739
help: 'Removes PREFIX from all migrated member names.\n'
@@ -77,6 +79,13 @@ class ModuleMigrator extends Migrator {
7779
Map<Uri, String> migrateFile(
7880
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
7981
var forwards = {for (var arg in argResults!['forward']) ForwardType(arg)};
82+
var builtInOnly = argResults!['built-in-only'] as bool;
83+
if (builtInOnly &&
84+
(argResults!.wasParsed('forward') ||
85+
argResults!.wasParsed('remove-prefix'))) {
86+
throw MigrationException('--forward and --remove-prefix may not be '
87+
'passed with --built-in-only.');
88+
}
8089
if (forwards.contains(ForwardType.prefixed) &&
8190
!argResults!.wasParsed('remove-prefix')) {
8291
throw MigrationException(
@@ -85,8 +94,10 @@ class ModuleMigrator extends Migrator {
8594
}
8695

8796
var references = References(importCache, stylesheet, importer);
88-
var visitor = _ModuleMigrationVisitor(importCache, references,
89-
globalResults!['load-path'] as List<String>, migrateDependencies,
97+
var visitor = _ModuleMigrationVisitor(
98+
importCache, references, globalResults!['load-path'] as List<String>,
99+
migrateDependencies: migrateDependencies,
100+
builtInOnly: builtInOnly,
90101
prefixesToRemove: (argResults!['remove-prefix'] as List<String>)
91102
.map((prefix) => prefix.replaceAll('_', '-')),
92103
forwards: forwards);
@@ -186,9 +197,6 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
186197
/// main migration pass is used.
187198
final References references;
188199

189-
/// Cache used to load stylesheets.
190-
final ImportCache importCache;
191-
192200
/// List of paths that stylesheets can be loaded from.
193201
final List<String> loadPaths;
194202

@@ -199,6 +207,9 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
199207
/// The values of the --forward flag.
200208
final Set<ForwardType> forwards;
201209

210+
/// Whether to migrate only global functions, leaving `@import` rules as-is.
211+
final bool builtInOnly;
212+
202213
/// Constructs a new module migration visitor.
203214
///
204215
/// [importCache] must be the same one used by [references].
@@ -210,22 +221,25 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
210221
/// the module migrator will filter out the dependencies' migration results.
211222
///
212223
/// This converts the OS-specific relative [loadPaths] to absolute URL paths.
213-
_ModuleMigrationVisitor(this.importCache, this.references,
214-
List<String> loadPaths, bool migrateDependencies,
215-
{Iterable<String> prefixesToRemove = const [], this.forwards = const {}})
224+
_ModuleMigrationVisitor(
225+
super.importCache, this.references, List<String> loadPaths,
226+
{required super.migrateDependencies,
227+
required this.builtInOnly,
228+
Iterable<String> prefixesToRemove = const [],
229+
this.forwards = const {}})
216230
: loadPaths = List.unmodifiable(
217231
loadPaths.map((path) => p.toUri(p.absolute(path)).path)),
218-
prefixesToRemove = UnmodifiableSetView(prefixesToRemove.toSet()),
219-
super(importCache, migrateDependencies);
232+
prefixesToRemove = UnmodifiableSetView(prefixesToRemove.toSet());
220233

221234
/// Checks which global declarations need to be renamed, then runs the
222235
/// migrator.
223236
@override
224237
Map<Uri, String> run(Stylesheet stylesheet, Importer importer) {
225-
references.globalDeclarations.forEach(_renameDeclaration);
238+
if (!builtInOnly) references.globalDeclarations.forEach(_renameDeclaration);
226239
var migrated = super.run(stylesheet, importer);
227240

228-
if (forwards.contains(ForwardType.importOnly) || _needsImportOnly) {
241+
if (!builtInOnly &&
242+
(forwards.contains(ForwardType.importOnly) || _needsImportOnly)) {
229243
var url = stylesheet.span.sourceUrl!;
230244
var importOnlyUrl = getImportOnlyUrl(url);
231245
var results = _generateImportOnly(url, importOnlyUrl);
@@ -597,7 +611,10 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
597611
/// Adds a namespace to any function call that requires it.
598612
@override
599613
void visitFunctionExpression(FunctionExpression node) {
600-
if (node.namespace != null) {
614+
var source = references.sources[node];
615+
if (node.namespace != null ||
616+
source is UseSource ||
617+
builtInOnly && source is! BuiltInSource) {
601618
super.visitFunctionExpression(node);
602619
return;
603620
}
@@ -631,12 +648,15 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
631648
// Warn for get-function calls without a static name.
632649
var nameArg =
633650
node.arguments.named['name'] ?? node.arguments.positional.first;
634-
if (nameArg is! StringExpression || nameArg.text.asPlain == null) {
651+
if ((nameArg is! StringExpression || nameArg.text.asPlain == null) &&
652+
!builtInOnly) {
635653
emitWarning(
636654
"get-function call may require \$module parameter", nameArg.span);
637655
return;
638656
}
639657

658+
if (builtInOnly && declaration != null) return;
659+
640660
_patchNamespaceForFunction(node, declaration, (namespace) {
641661
var beforeParen = node.span.end.offset - 1;
642662
addPatch(Patch(node.span.file.span(beforeParen, beforeParen),
@@ -776,14 +796,21 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
776796
}
777797
}
778798
if (ruleUrl != null) {
779-
if (_useAllowed) {
799+
if (builtInOnly) {
800+
if (migrateDependencies) {
801+
_upstreamStylesheets.add(currentUrl);
802+
visitDependency(ruleUrl, import.span);
803+
_upstreamStylesheets.remove(currentUrl);
804+
}
805+
} else if (_useAllowed) {
780806
migratedRules.addAll(_migrateImportToRules(ruleUrl, import.span));
781807
} else {
782808
migratedRules.add(_migrateImportToLoadCss(ruleUrl, import.span)
783809
.replaceAll('\n', '\n$indent'));
784810
}
785811
}
786812
}
813+
if (builtInOnly) return;
787814

788815
rulesText = migratedRules.join('$semicolon\n$indent');
789816
if (rulesText.isEmpty) {
@@ -1071,6 +1098,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
10711098
_useAllowed = false;
10721099
super.visitIncludeRule(node);
10731100
if (node.namespace != null) return;
1101+
if (builtInOnly && references.sources[node] is! BuiltInSource) return;
10741102

10751103
var declaration = references.mixins[node];
10761104
if (declaration == null) {
@@ -1089,7 +1117,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
10891117
@override
10901118
void visitMixinRule(MixinRule node) {
10911119
_useAllowed = false;
1092-
_renameReference(nameSpan(node), MemberDeclaration(node));
1120+
if (!builtInOnly) _renameReference(nameSpan(node), MemberDeclaration(node));
10931121
super.visitMixinRule(node);
10941122
}
10951123

@@ -1119,6 +1147,7 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
11191147
@override
11201148
void visitVariableExpression(VariableExpression node) {
11211149
if (node.namespace != null) return;
1150+
if (builtInOnly && references.sources[node] is! BuiltInSource) return;
11221151
var declaration = references.variables[node];
11231152
if (declaration == null) {
11241153
// TODO(jathak): Error here as part of fixing #182.
@@ -1145,6 +1174,10 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
11451174
/// renaming or namespacing if necessary.
11461175
@override
11471176
void visitVariableDeclaration(VariableDeclaration node) {
1177+
if (builtInOnly) {
1178+
super.visitVariableDeclaration(node);
1179+
return;
1180+
}
11481181
var declaration = MemberDeclaration(node);
11491182
var defaultDeclaration =
11501183
references.defaultVariableDeclarations[declaration];

lib/src/migrators/module/built_in_functions.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const builtInFunctionModules = {
2222
"complement": "color",
2323
"invert": "color",
2424
"alpha": "color",
25+
"opacity": "color",
2526
"opacify": "color",
2627
"fade-in": "color",
2728
"transparentize": "color",
@@ -43,6 +44,7 @@ const builtInFunctionModules = {
4344
"is-superselector": "selector",
4445
"simple-selectors": "selector",
4546
"selector-parse": "selector",
47+
"selector-extend": "selector",
4648
"percentage": "math",
4749
"round": "math",
4850
"ceil": "math",
@@ -62,6 +64,7 @@ const builtInFunctionModules = {
6264
"zip": "list",
6365
"index": "list",
6466
"list-separator": "list",
67+
"is-bracketed": "list",
6568
"feature-exists": "meta",
6669
"variable-exists": "meta",
6770
"global-variable-exists": "meta",
@@ -100,6 +103,7 @@ const builtInFunctionNameChanges = {
100103
"selector-replace": "replace",
101104
"selector-unify": "unify",
102105
"selector-parse": "parse",
106+
"selector-extend": "extend",
103107
"unitless": "is-unitless",
104108
"comparable": "compatible",
105109
"list-separator": "separator",

lib/src/migrators/module/references.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,13 +383,19 @@ class _ReferenceVisitor extends ScopedAstVisitor {
383383
void visitUseRule(UseRule node) {
384384
super.visitUseRule(node);
385385
var namespace = node.namespace;
386-
if (namespace == null) return;
387386
if (node.url.scheme == 'sass') {
388-
_namespaces[namespace] = node.url;
387+
if (namespace != null) _namespaces[namespace] = node.url;
389388
return;
390389
}
391390
var canonicalUrl = _loadUseOrForward(node.url, node);
392-
_namespaces[namespace] = canonicalUrl;
391+
if (namespace != null) {
392+
_namespaces[namespace] = canonicalUrl;
393+
} else {
394+
var moduleScope = _moduleScopes[canonicalUrl]!;
395+
currentScope.variables.addAll(moduleScope.variables);
396+
currentScope.mixins.addAll(moduleScope.mixins);
397+
currentScope.functions.addAll(moduleScope.functions);
398+
}
393399

394400
// `_moduleSources[canonicalUrl]` is set in `_loadUseOrForward`.
395401
var moduleSources = _moduleSources[canonicalUrl]!;

lib/src/migrators/namespace.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ class NamespaceMigrator extends Migrator {
3838
var renamer = Renamer<UseRule>(argResults!['rename'].join('\n'),
3939
{'': ((rule) => rule.namespace!), 'url': (rule) => rule.url.toString()},
4040
sourceUrl: '--rename');
41-
var visitor = _NamespaceMigrationVisitor(renamer,
42-
argResults!['force'] as bool, importCache, migrateDependencies);
41+
var visitor = _NamespaceMigrationVisitor(
42+
renamer, argResults!['force'] as bool, importCache,
43+
migrateDependencies: migrateDependencies);
4344
var result = visitor.run(stylesheet, importer);
4445
missingDependencies.addAll(visitor.missingDependencies);
4546
return result;
@@ -62,9 +63,8 @@ class _NamespaceMigrationVisitor extends MigrationVisitor {
6263
assertInStylesheet(__usedNamespaces, '_usedNamespaces');
6364
Set<String>? __usedNamespaces;
6465

65-
_NamespaceMigrationVisitor(this.renamer, this.forceRename,
66-
ImportCache importCache, bool migrateDependencies)
67-
: super(importCache, migrateDependencies);
66+
_NamespaceMigrationVisitor(this.renamer, this.forceRename, super.importCache,
67+
{required super.migrateDependencies});
6868

6969
@override
7070
void visitStylesheet(Stylesheet node) {

lib/src/migrators/strict_unary.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@ class StrictUnaryMigrator extends Migrator {
1919
@override
2020
Map<Uri, String> migrateFile(
2121
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
22-
var visitor = _UnaryMigrationVisitor(importCache, migrateDependencies);
22+
var visitor = _UnaryMigrationVisitor(importCache,
23+
migrateDependencies: migrateDependencies);
2324
var result = visitor.run(stylesheet, importer);
2425
missingDependencies.addAll(visitor.missingDependencies);
2526
return result;
2627
}
2728
}
2829

2930
class _UnaryMigrationVisitor extends MigrationVisitor {
30-
_UnaryMigrationVisitor(ImportCache importCache, bool migrateDependencies)
31-
: super(importCache, migrateDependencies);
31+
_UnaryMigrationVisitor(super.importCache,
32+
{required super.migrateDependencies});
3233

3334
@override
3435
void visitBinaryOperationExpression(BinaryOperationExpression node) {

0 commit comments

Comments
 (0)