Skip to content

Commit 2800cca

Browse files
jathaknex3
andauthored
Properly migrate orphan import-only files (#162)
Fixes #160. Co-authored-by: Natalie Weizenbaum <[email protected]>
1 parent e255762 commit 2800cca

10 files changed

+128
-18
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939

4040
# Static checks
4141
- dart_task: {dartanalyzer: --fatal-warnings --fatal-hints ./}
42-
- dart_task: dartfmt
42+
- dart_task: {dartfmt: sdk}
4343

4444
## Deployment
4545

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 1.2.4
2+
3+
### Module Migrator
4+
5+
* The migrator no longer crashes when it encounters an import-only file without
6+
a corresponding regular file.
7+
* If an import-only file does not forward its corresponding regular file, the
8+
migrator no longer includes a `@use` rule for it.
9+
110
## 1.2.3
211

312
* Updates help text to use the correct binary name (`sass-migrator`).

lib/src/migrators/module.dart

+15-11
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,11 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
447447
// If there was already a blank line after the insertion point, or the
448448
// insertion point was at the end of the file, remove the additional line
449449
// break at the end of the extra rules.
450-
if (insertionPoint == node.span.start) extras = '$extras\n';
450+
extras = '$extras\n';
451451
var whitespace = extendThroughWhitespace(insertionPoint.pointSpan());
452452
if (whitespace.text.contains('\n\n') || whitespace.end == node.span.end) {
453453
extras = extras.substring(0, extras.length - 1);
454454
}
455-
if (insertionPoint == _afterLastImport) extras = '\n$extras';
456455
addPatch(Patch.insert(insertionPoint, extras));
457456
}
458457

@@ -746,7 +745,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
746745
var imports =
747746
partitionOnType<Import, StaticImport, DynamicImport>(node.imports);
748747
var staticImports = imports.item1;
749-
var dynamicImports = imports.item2;
748+
var dynamicImports = imports.item2.where((import) {
749+
var url = importCache
750+
.canonicalize(Uri.parse(import.url),
751+
baseImporter: importer, forImport: true)
752+
?.item2;
753+
return !references.orphanImportOnlyFiles.contains(url);
754+
});
750755

751756
var start = node.span.start;
752757
var first = true;
@@ -760,6 +765,10 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
760765
_migrateImport(import, start);
761766
}
762767

768+
var spanWithNewline = extendForward(
769+
extendThroughWhitespace(node.span), '$_semicolonIfNotIndented\n') ??
770+
node.span;
771+
763772
if (staticImports.isNotEmpty) {
764773
if (dynamicImports.isNotEmpty) {
765774
addPatch(Patch.insert(start, "$_semicolonIfNotIndented\n"));
@@ -777,18 +786,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
777786
extendForward(extended, ",") ?? extendBackward(extended, ",")));
778787
}
779788
} else {
780-
addPatch(patchDelete(node.span));
789+
addPatch(
790+
patchDelete(dynamicImports.isEmpty ? spanWithNewline : node.span));
781791
}
782792

783793
if (_useAllowed) {
784794
_beforeFirstImport ??= node.span.start;
785-
if (currentUrl.path.endsWith('.sass')) {
786-
_afterLastImport = node.span.end;
787-
} else {
788-
_afterLastImport =
789-
extendForward(extendThroughWhitespace(node.span), ';')?.end ??
790-
node.span.end;
791-
}
795+
_afterLastImport = spanWithNewline.end;
792796
}
793797
}
794798

lib/src/migrators/module/references.dart

+24-3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ class References {
9090
/// map to the [ReferenceSource] for the `sass:meta` module).
9191
final Map<SassNode, ReferenceSource> sources;
9292

93+
/// The set of import-only files that do not directly depend on their regular
94+
/// counterparts.
95+
final Set<Uri> orphanImportOnlyFiles;
96+
9397
/// An iterable of all member declarations.
9498
Iterable<MemberDeclaration> get allDeclarations =>
9599
variables.values.followedBy(mixins.values).followedBy(functions.values);
@@ -149,7 +153,8 @@ class References {
149153
getFunctionReferences,
150154
Set<MemberDeclaration> globalDeclarations,
151155
Map<MemberDeclaration, Set<Uri>> libraries,
152-
Map<SassNode, ReferenceSource> sources)
156+
Map<SassNode, ReferenceSource> sources,
157+
Set<Uri> orphanImportOnlyFiles)
153158
: variables = UnmodifiableBidirectionalMapView(variables),
154159
variableReassignments =
155160
UnmodifiableBidirectionalMapView(variableReassignments),
@@ -162,7 +167,8 @@ class References {
162167
globalDeclarations = UnmodifiableSetView(globalDeclarations),
163168
libraries = UnmodifiableMapView(
164169
mapMap(libraries, value: (_, urls) => UnmodifiableSetView(urls))),
165-
sources = UnmodifiableMapView(sources);
170+
sources = UnmodifiableMapView(sources),
171+
orphanImportOnlyFiles = UnmodifiableSetView(orphanImportOnlyFiles);
166172

167173
/// Constructs a new [References] object based on a [stylesheet] (imported by
168174
/// [importer]) and its dependencies.
@@ -188,6 +194,7 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
188194
final _globalDeclarations = <MemberDeclaration>{};
189195
final _libraries = <MemberDeclaration, Set<Uri>>{};
190196
final _sources = <SassNode, ReferenceSource>{};
197+
final _orphanImportOnlyFiles = <Uri>{};
191198

192199
/// The current global scope.
193200
///
@@ -243,6 +250,12 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
243250
/// stylesheet.
244251
Importer _importer;
245252

253+
/// If the current stylesheet is an import-only file, this starts as true and
254+
/// is changed to false if it forwards its regular counterpart.
255+
///
256+
/// This is always false for regular files.
257+
bool _isOrphanImportOnly;
258+
246259
/// Cache used to load stylesheets.
247260
ImportCache importCache;
248261

@@ -276,7 +289,8 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
276289
_getFunctionReferences,
277290
_globalDeclarations,
278291
_libraries,
279-
_sources);
292+
_sources,
293+
_orphanImportOnlyFiles);
280294
}
281295

282296
/// Checks any remaining [_unresolvedReferences] to see if they match a
@@ -321,9 +335,13 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
321335
void visitStylesheet(Stylesheet node) {
322336
var oldNamespaces = _namespaces;
323337
var oldUrl = _currentUrl;
338+
var oldOrphaned = _isOrphanImportOnly;
324339
_namespaces = {};
325340
_currentUrl = node.span.sourceUrl;
341+
_isOrphanImportOnly = isImportOnlyFile(_currentUrl);
326342
super.visitStylesheet(node);
343+
if (_isOrphanImportOnly) _orphanImportOnlyFiles.add(_currentUrl);
344+
_isOrphanImportOnly = oldOrphaned;
327345
_namespaces = oldNamespaces;
328346
_currentUrl = oldUrl;
329347
}
@@ -436,6 +454,9 @@ class _ReferenceVisitor extends RecursiveAstVisitor {
436454
void visitForwardRule(ForwardRule node) {
437455
super.visitForwardRule(node);
438456
var canonicalUrl = _loadUseOrForward(node.url, node);
457+
if (_isOrphanImportOnly && _currentUrl == getImportOnlyUrl(canonicalUrl)) {
458+
_isOrphanImportOnly = false;
459+
}
439460
var moduleScope = _moduleScopes[canonicalUrl];
440461
for (var declaration in moduleScope.variables.values) {
441462
if (declaration.member is! VariableDeclaration) {

lib/src/migrators/module/unreferencable_type.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ class UnreferencableType {
3333
MigrationException toException(SassNode reference, Uri source) {
3434
var type = reference is IncludeRule
3535
? 'mixin'
36-
: reference is FunctionExpression ? 'function' : 'variable';
36+
: reference is FunctionExpression
37+
? 'function'
38+
: 'variable';
3739
var url = p.prettyUri(source);
3840
switch (this) {
3941
case fromImporter:

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass_migrator
2-
version: 1.2.3
2+
version: 1.2.4
33
description: A tool for running migrations on Sass files
44
author: Jennifer Thakar <[email protected]>
55
homepage: https://github.com/sass/migrator
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<==> arguments
2+
--migrate-deps
3+
4+
<==> input/entrypoint.scss
5+
@import "old";
6+
a {
7+
b: $lib-variable;
8+
}
9+
10+
<==> input/_old.scss
11+
@error "Use 'new'";
12+
13+
<==> input/_old.import.scss
14+
@forward "new" as lib-*;
15+
16+
<==> input/_new.scss
17+
$variable: green;
18+
19+
<==> output/entrypoint.scss
20+
@use "new";
21+
22+
a {
23+
b: new.$variable;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<==> arguments
2+
--migrate-deps
3+
4+
<==> input/entrypoint.scss
5+
@import "old";
6+
a {
7+
b: $lib-variable;
8+
}
9+
10+
<==> input/_old.import.scss
11+
@forward "new" as lib-*;
12+
13+
<==> input/_new.scss
14+
$variable: green;
15+
16+
<==> output/entrypoint.scss
17+
@use "new";
18+
19+
a {
20+
b: new.$variable;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<==> arguments
2+
--migrate-deps
3+
4+
<==> input/entrypoint.scss
5+
@import "old";
6+
a {
7+
b: $lib-variable1;
8+
c: $lib-variable2;
9+
}
10+
11+
<==> input/_old.import.scss
12+
@forward "a" as lib-*;
13+
@forward "b" as lib-*;
14+
15+
<==> input/_a.scss
16+
$variable1: green;
17+
18+
<==> input/_b.scss
19+
$variable2: blue;
20+
21+
<==> output/entrypoint.scss
22+
@use "a";
23+
@use "b";
24+
25+
a {
26+
b: a.$variable1;
27+
c: b.$variable2;
28+
}

test/utils.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ Future<void> _testHrx(File hrxFile, String migrator) async {
5757
migrator,
5858
'--no-unicode',
5959
...files.arguments,
60-
for (var path in files.input.keys) if (path.startsWith("entrypoint")) path
60+
for (var path in files.input.keys)
61+
if (path.startsWith("entrypoint")) path
6162
]);
6263

6364
if (files.expectedLog != null) {

0 commit comments

Comments
 (0)