Skip to content

Commit

Permalink
Merge pull request #1438 from sass/extend-bugs
Browse files Browse the repository at this point in the history
Fix a couple bugs with @extend
  • Loading branch information
nex3 committed Aug 16, 2021
2 parents 2a03907 + 707db69 commit 390deed
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

* Properly parse backslash escapes within `url()` expressions.

* Fix a couple bugs where `@extend`s could be marked as unsatisfied when
multiple identical `@extend`s extended selectors across `@use` rules.

### Command Line Interface

* Strip CRLF newlines from snippets of the original stylesheet that are included
Expand Down
17 changes: 7 additions & 10 deletions lib/src/extend/extension_store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class ExtensionStore {
/// Extends [this] with all the extensions in [extensions].
///
/// These extensions will extend all selectors already in [this], but they
/// will *not* extend other extensions from [extenders].
/// will *not* extend other extensions from [extensionStores].
void addExtensions(Iterable<ExtensionStore> extensionStores) {
// Extensions already in [this] whose extenders are extended by
// [extensions], and thus which need to be updated.
Expand Down Expand Up @@ -445,21 +445,18 @@ class ExtensionStore {
// Add [newSources] to [_extensions].
var existingSources = _extensions[target];
if (existingSources == null) {
_extensions[target] = newSources;
_extensions[target] = Map.of(newSources);
if (extensionsForTarget != null || selectorsForTarget != null) {
(newExtensions ??= {})[target] = newSources;
(newExtensions ??= {})[target] = Map.of(newSources);
}
} else {
newSources.forEach((extender, extension) {
// If [extender] already extends [target] in [_extensions], we don't
// need to re-run the extension.
if (existingSources.containsKey(extender)) return;
existingSources[extender] = extension;
extension = existingSources.putOrMerge(
extender, extension, MergedExtension.merge);

if (extensionsForTarget != null || selectorsForTarget != null) {
(newExtensions ??= {})
.putIfAbsent(target, () => {})
.putIfAbsent(extender, () => extension);
(newExtensions ??= {}).putIfAbsent(target, () => {})[extender] =
extension;
}
});
}
Expand Down
10 changes: 5 additions & 5 deletions lib/src/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ JsError _wrapException(Object exception) {
var url = exception.span.sourceUrl;
if (url == null) {
file = 'stdin';
} else if (url.scheme == 'file') {
file = p.fromUri(url);
} else {
file = url.toString();
}
} else if (url.scheme == 'file') {
file = p.fromUri(url);
} else {
file = url.toString();
}

return _newRenderError(exception.toString().replaceFirst("Error: ", ""),
line: exception.span.start.line + 1,
Expand Down
12 changes: 12 additions & 0 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,15 @@ extension SpanExtensions on FileSpan {
: file.span(this.start.offset + start, this.start.offset + end + 1);
}
}

extension MapExtension<K, V> on Map<K, V> {
/// If [this] doesn't contain the given [key], sets that key to [value] and
/// returns it.
///
/// Otherwise, calls [merge] with the existing value and [value] and sets
/// [key] to the result.
V putOrMerge(K key, V value, V Function(V oldValue, V newValue) merge) =>
containsKey(key)
? this[key] = merge(this[key]!, value)
: this[key] = value;
}
12 changes: 6 additions & 6 deletions test/node_api/importer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -706,14 +706,14 @@ void main() {
});

test("it occurs in a file with a custom URL scheme", () {
var error =
renderSyncError(RenderOptions(
data: "@import 'foo:bar'",
importer: allowInterop(expectAsync2((String _, void __) {
return NodeImporterResult(contents: '@error "oh no";');
var error = renderSyncError(RenderOptions(
data: "@import 'foo:bar'",
importer: allowInterop(expectAsync2((String _, void __) {
return NodeImporterResult(contents: '@error "oh no";');
}))));

expect(error,
expect(
error,
toStringAndMessageEqual("\"oh no\"\n"
" ╷\n"
"1 │ @error \"oh no\";\n"
Expand Down

0 comments on commit 390deed

Please sign in to comment.