Clone CSS after importing it

This commit is contained in:
Natalie Weizenbaum 2019-03-30 16:44:06 -07:00
parent 11c95900c8
commit d6f319f5b4
7 changed files with 60 additions and 21 deletions

View File

@ -4,6 +4,7 @@
import 'dart:async';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'ast/css.dart';
@ -621,6 +622,8 @@ class AsyncEnvironment {
/// A module that represents the top-level members defined in an [Environment].
class _EnvironmentModule implements AsyncModule {
Uri get url => css.span.sourceUrl;
final List<AsyncModule> upstream;
final Map<String, Value> variables;
final Map<String, AstNode> variableNodes;
@ -666,4 +669,6 @@ class _EnvironmentModule implements AsyncModule {
return _EnvironmentModule(
_environment, newCssAndExtender.item1, newCssAndExtender.item2);
}
String toString() => p.prettyUri(css.span.sourceUrl);
}

View File

@ -12,6 +12,12 @@ import 'value.dart';
/// The interface for a Sass module.
abstract class AsyncModule {
/// The canonical URL for this module's source file.
///
/// This may be `null` if the module was loaded from a string without a URL
/// provided.
Uri get url;
/// Modules that this module uses.
List<AsyncModule> get upstream;

View File

@ -5,10 +5,11 @@
// DO NOT EDIT. This file was generated from async_environment.dart.
// See tool/synchronize.dart for details.
//
// Checksum: 36876e7e932a30409d59d0fa256ad02d25934aab
// Checksum: 097eb94cd15103bf4bef739a61e61414db4b55b1
//
// ignore_for_file: unused_import
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'ast/css.dart';
@ -623,6 +624,8 @@ class Environment {
/// A module that represents the top-level members defined in an [Environment].
class _EnvironmentModule implements Module {
Uri get url => css.span.sourceUrl;
final List<Module> upstream;
final Map<String, Value> variables;
final Map<String, AstNode> variableNodes;
@ -668,4 +671,6 @@ class _EnvironmentModule implements Module {
return _EnvironmentModule(
_environment, newCssAndExtender.item1, newCssAndExtender.item2);
}
String toString() => p.prettyUri(css.span.sourceUrl);
}

View File

@ -398,6 +398,7 @@ class Extender {
Map<SimpleSelector, Map<ComplexSelector, Extension>> newExtensions;
for (var extender in extenders) {
if (extender.isEmpty) continue;
extender._extensions.forEach((target, newSources) {
// Private selectors can't be extended across module boundaries.
if (target is PlaceholderSelector && target.isPrivate) return;

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_module.dart.
// See tool/synchronize.dart for details.
//
// Checksum: e85903dbb32318c558ca4132a78dca55ee617280
// Checksum: 895440529b78f1ef2830cf105af00dba0755e947
//
// ignore_for_file: unused_import
@ -19,6 +19,12 @@ import 'value.dart';
/// The interface for a Sass module.
abstract class Module {
/// The canonical URL for this module's source file.
///
/// This may be `null` if the module was loaded from a string without a URL
/// provided.
Uri get url;
/// Modules that this module uses.
List<Module> get upstream;

View File

@ -439,7 +439,10 @@ class _EvaluateVisitor
/// modules transitively used by [root].
///
/// This also applies each module's extensions to its upstream modules.
CssStylesheet _combineCss(AsyncModule root) {
///
/// If [clone] is `true`, this will copy the modules before extending them so
/// that they don't modify [root] or its dependencies.
CssStylesheet _combineCss(AsyncModule root, {bool clone = false}) {
// TODO(nweiz): short-circuit if no upstream modules (transitively) include
// any CSS.
if (root.upstream.isEmpty) {
@ -454,6 +457,9 @@ class _EvaluateVisitor
}
var sortedModules = _topologicalModules(root);
if (clone) {
sortedModules = sortedModules.map((module) => module.cloneCss()).toList();
}
_extendModules(sortedModules);
// The imports (and comments between them) that should be included at the
@ -476,11 +482,11 @@ class _EvaluateVisitor
/// Extends the selectors in each module with the extensions defined in
/// downstream modules.
void _extendModules(List<AsyncModule> sortedModules) {
// All the extenders directly downstream of a given module. It's important
// that we create this in topological order, so that by the time we're
// processing a module we've already filled in all its downstream extenders
// and we can use them to extend that module.
var downstreamExtenders = <AsyncModule, List<Extender>>{};
// All the extenders directly downstream of a given module (indexed by its
// canonical URL). It's important that we create this in topological order,
// so that by the time we're processing a module we've already filled in all
// its downstream extenders and we can use them to extend that module.
var downstreamExtenders = <Uri, List<Extender>>{};
/// Extensions that haven't yet been satisfied by some upstream module. This
/// adds extensions when they're defined but not satisfied, and removes them
@ -498,13 +504,13 @@ class _EvaluateVisitor
unsatisfiedExtensions.addAll(module.extender.extensionsWhereTarget(
(target) => !originalSelectors.contains(target)));
var extenders = downstreamExtenders[module];
var extenders = downstreamExtenders[module.url];
if (extenders != null) module.extender.addExtensions(extenders);
if (module.extender.isEmpty) continue;
for (var upstream in module.upstream) {
downstreamExtenders
.putIfAbsent(upstream, () => [])
.putIfAbsent(upstream.url, () => [])
.add(module.extender);
}
@ -1026,7 +1032,9 @@ class _EvaluateVisitor
// the CSS from modules used by [stylesheet].
var module = environment.toModule(
CssStylesheet(const [], stylesheet.span), Extender.empty);
if (module.transitivelyContainsCss) await _combineCss(module).accept(this);
if (module.transitivelyContainsCss) {
await _combineCss(module, clone: true).accept(this);
}
var visitor = _ImportedCssVisitor(this);
for (var child in children) {

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/synchronize.dart for details.
//
// Checksum: c7d7b5c49b3b2d09edd180440fc10582f53fdafb
// Checksum: 0f4f00db38a7f2a60771d3d50652cbfdeb322e52
//
// ignore_for_file: unused_import
@ -447,7 +447,10 @@ class _EvaluateVisitor
/// modules transitively used by [root].
///
/// This also applies each module's extensions to its upstream modules.
CssStylesheet _combineCss(Module root) {
///
/// If [clone] is `true`, this will copy the modules before extending them so
/// that they don't modify [root] or its dependencies.
CssStylesheet _combineCss(Module root, {bool clone = false}) {
// TODO(nweiz): short-circuit if no upstream modules (transitively) include
// any CSS.
if (root.upstream.isEmpty) {
@ -462,6 +465,9 @@ class _EvaluateVisitor
}
var sortedModules = _topologicalModules(root);
if (clone) {
sortedModules = sortedModules.map((module) => module.cloneCss()).toList();
}
_extendModules(sortedModules);
// The imports (and comments between them) that should be included at the
@ -484,11 +490,11 @@ class _EvaluateVisitor
/// Extends the selectors in each module with the extensions defined in
/// downstream modules.
void _extendModules(List<Module> sortedModules) {
// All the extenders directly downstream of a given module. It's important
// that we create this in topological order, so that by the time we're
// processing a module we've already filled in all its downstream extenders
// and we can use them to extend that module.
var downstreamExtenders = <Module, List<Extender>>{};
// All the extenders directly downstream of a given module (indexed by its
// canonical URL). It's important that we create this in topological order,
// so that by the time we're processing a module we've already filled in all
// its downstream extenders and we can use them to extend that module.
var downstreamExtenders = <Uri, List<Extender>>{};
/// Extensions that haven't yet been satisfied by some upstream module. This
/// adds extensions when they're defined but not satisfied, and removes them
@ -506,13 +512,13 @@ class _EvaluateVisitor
unsatisfiedExtensions.addAll(module.extender.extensionsWhereTarget(
(target) => !originalSelectors.contains(target)));
var extenders = downstreamExtenders[module];
var extenders = downstreamExtenders[module.url];
if (extenders != null) module.extender.addExtensions(extenders);
if (module.extender.isEmpty) continue;
for (var upstream in module.upstream) {
downstreamExtenders
.putIfAbsent(upstream, () => [])
.putIfAbsent(upstream.url, () => [])
.add(module.extender);
}
@ -1029,7 +1035,9 @@ class _EvaluateVisitor
// the CSS from modules used by [stylesheet].
var module = environment.toModule(
CssStylesheet(const [], stylesheet.span), Extender.empty);
if (module.transitivelyContainsCss) _combineCss(module).accept(this);
if (module.transitivelyContainsCss) {
_combineCss(module, clone: true).accept(this);
}
var visitor = _ImportedCssVisitor(this);
for (var child in children) {