mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-22 22:02:00 +01:00
Emit CSS for used modules (#620)
This commit is contained in:
parent
b66e1bad28
commit
c98cfd53b0
@ -29,6 +29,10 @@ class AsyncEnvironment {
|
||||
/// This is `null` if there are no namespaceless modules.
|
||||
Set<AsyncModule> _globalModules;
|
||||
|
||||
/// Modules from both [_modules] and [_global], in the order in which they
|
||||
/// were `@use`d.
|
||||
final List<AsyncModule> _allModules;
|
||||
|
||||
/// A list of variables defined at each lexical scope level.
|
||||
///
|
||||
/// Each scope maps the names of declared variables to their values. These
|
||||
@ -122,6 +126,7 @@ class AsyncEnvironment {
|
||||
AsyncEnvironment({bool sourceMap = false})
|
||||
: _modules = {},
|
||||
_globalModules = null,
|
||||
_allModules = [],
|
||||
_variables = [normalizedMap()],
|
||||
_variableNodes = sourceMap ? [normalizedMap()] : null,
|
||||
_variableIndices = normalizedMap(),
|
||||
@ -132,8 +137,15 @@ class AsyncEnvironment {
|
||||
coreFunctions.forEach(setFunction);
|
||||
}
|
||||
|
||||
AsyncEnvironment._(this._modules, this._globalModules, this._variables,
|
||||
this._variableNodes, this._functions, this._mixins, this._content)
|
||||
AsyncEnvironment._(
|
||||
this._modules,
|
||||
this._globalModules,
|
||||
this._allModules,
|
||||
this._variables,
|
||||
this._variableNodes,
|
||||
this._functions,
|
||||
this._mixins,
|
||||
this._content)
|
||||
// Lazily fill in the indices rather than eagerly copying them from the
|
||||
// existing environment in closure() because the copying took a lot of
|
||||
// time and was rarely helpful. This saves a bunch of time on Susy's
|
||||
@ -150,6 +162,7 @@ class AsyncEnvironment {
|
||||
AsyncEnvironment closure() => AsyncEnvironment._(
|
||||
_modules,
|
||||
_globalModules,
|
||||
_allModules,
|
||||
_variables.toList(),
|
||||
_variableNodes?.toList(),
|
||||
_functions.toList(),
|
||||
@ -160,8 +173,10 @@ class AsyncEnvironment {
|
||||
///
|
||||
/// The returned environment shares this environment's global variables,
|
||||
/// functions, and mixins, but not its modules.
|
||||
AsyncEnvironment global() => AsyncEnvironment._({},
|
||||
AsyncEnvironment global() => AsyncEnvironment._(
|
||||
{},
|
||||
null,
|
||||
[],
|
||||
_variables.toList(),
|
||||
_variableNodes?.toList(),
|
||||
_functions.toList(),
|
||||
@ -180,6 +195,7 @@ class AsyncEnvironment {
|
||||
if (namespace == null) {
|
||||
_globalModules ??= Set();
|
||||
_globalModules.add(module);
|
||||
_allModules.add(module);
|
||||
|
||||
for (var name in _variables.first.keys) {
|
||||
if (module.variables.containsKey(name)) {
|
||||
@ -195,6 +211,7 @@ class AsyncEnvironment {
|
||||
}
|
||||
|
||||
_modules[namespace] = module;
|
||||
_allModules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,6 +617,7 @@ class AsyncEnvironment {
|
||||
|
||||
/// A module that represents the top-level members defined in an [Environment].
|
||||
class _EnvironmentModule implements AsyncModule {
|
||||
final List<AsyncModule> upstream;
|
||||
final Map<String, Value> variables;
|
||||
final Map<String, AstNode> variableNodes;
|
||||
final Map<String, AsyncCallable> functions;
|
||||
@ -612,7 +630,8 @@ class _EnvironmentModule implements AsyncModule {
|
||||
// TODO(nweiz): Use custom [UnmodifiableMapView]s that forbid access to
|
||||
// private members.
|
||||
_EnvironmentModule(this._environment, this.css)
|
||||
: variables = PublicMemberMap(_environment._variables.first),
|
||||
: upstream = _environment._allModules,
|
||||
variables = PublicMemberMap(_environment._variables.first),
|
||||
variableNodes = _environment._variableNodes == null
|
||||
? null
|
||||
: PublicMemberMap(_environment._variableNodes.first),
|
||||
|
@ -11,6 +11,9 @@ import 'value.dart';
|
||||
|
||||
/// The interface for a Sass module.
|
||||
abstract class AsyncModule {
|
||||
/// Modules that this module uses.
|
||||
List<AsyncModule> get upstream;
|
||||
|
||||
/// The module's variables.
|
||||
Map<String, Value> get variables;
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_environment.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: 77b03257b1770270e2d1f269281bdaab3f9f1ae9
|
||||
// Checksum: e1d3693a4ede6e31d259efb13148a669be01e98c
|
||||
//
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
@ -34,6 +34,10 @@ class Environment {
|
||||
/// This is `null` if there are no namespaceless modules.
|
||||
Set<Module> _globalModules;
|
||||
|
||||
/// Modules from both [_modules] and [_global], in the order in which they
|
||||
/// were `@use`d.
|
||||
final List<Module> _allModules;
|
||||
|
||||
/// A list of variables defined at each lexical scope level.
|
||||
///
|
||||
/// Each scope maps the names of declared variables to their values. These
|
||||
@ -127,6 +131,7 @@ class Environment {
|
||||
Environment({bool sourceMap = false})
|
||||
: _modules = {},
|
||||
_globalModules = null,
|
||||
_allModules = [],
|
||||
_variables = [normalizedMap()],
|
||||
_variableNodes = sourceMap ? [normalizedMap()] : null,
|
||||
_variableIndices = normalizedMap(),
|
||||
@ -137,8 +142,15 @@ class Environment {
|
||||
coreFunctions.forEach(setFunction);
|
||||
}
|
||||
|
||||
Environment._(this._modules, this._globalModules, this._variables,
|
||||
this._variableNodes, this._functions, this._mixins, this._content)
|
||||
Environment._(
|
||||
this._modules,
|
||||
this._globalModules,
|
||||
this._allModules,
|
||||
this._variables,
|
||||
this._variableNodes,
|
||||
this._functions,
|
||||
this._mixins,
|
||||
this._content)
|
||||
// Lazily fill in the indices rather than eagerly copying them from the
|
||||
// existing environment in closure() because the copying took a lot of
|
||||
// time and was rarely helpful. This saves a bunch of time on Susy's
|
||||
@ -155,6 +167,7 @@ class Environment {
|
||||
Environment closure() => Environment._(
|
||||
_modules,
|
||||
_globalModules,
|
||||
_allModules,
|
||||
_variables.toList(),
|
||||
_variableNodes?.toList(),
|
||||
_functions.toList(),
|
||||
@ -165,8 +178,10 @@ class Environment {
|
||||
///
|
||||
/// The returned environment shares this environment's global variables,
|
||||
/// functions, and mixins, but not its modules.
|
||||
Environment global() => Environment._({},
|
||||
Environment global() => Environment._(
|
||||
{},
|
||||
null,
|
||||
[],
|
||||
_variables.toList(),
|
||||
_variableNodes?.toList(),
|
||||
_functions.toList(),
|
||||
@ -185,6 +200,7 @@ class Environment {
|
||||
if (namespace == null) {
|
||||
_globalModules ??= Set();
|
||||
_globalModules.add(module);
|
||||
_allModules.add(module);
|
||||
|
||||
for (var name in _variables.first.keys) {
|
||||
if (module.variables.containsKey(name)) {
|
||||
@ -200,6 +216,7 @@ class Environment {
|
||||
}
|
||||
|
||||
_modules[namespace] = module;
|
||||
_allModules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,6 +619,7 @@ class Environment {
|
||||
|
||||
/// A module that represents the top-level members defined in an [Environment].
|
||||
class _EnvironmentModule implements Module {
|
||||
final List<Module> upstream;
|
||||
final Map<String, Value> variables;
|
||||
final Map<String, AstNode> variableNodes;
|
||||
final Map<String, Callable> functions;
|
||||
@ -614,7 +632,8 @@ class _EnvironmentModule implements Module {
|
||||
// TODO(nweiz): Use custom [UnmodifiableMapView]s that forbid access to
|
||||
// private members.
|
||||
_EnvironmentModule(this._environment, this.css)
|
||||
: variables = PublicMemberMap(_environment._variables.first),
|
||||
: upstream = _environment._allModules,
|
||||
variables = PublicMemberMap(_environment._variables.first),
|
||||
variableNodes = _environment._variableNodes == null
|
||||
? null
|
||||
: PublicMemberMap(_environment._variableNodes.first),
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_module.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: 5608be0fdb1bff974611b75d1bbcb364a15d4df2
|
||||
// Checksum: 759037174212e69cd0d9a7ebf55f6ee9f66072e8
|
||||
//
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
@ -18,6 +18,9 @@ import 'value.dart';
|
||||
|
||||
/// The interface for a Sass module.
|
||||
abstract class Module {
|
||||
/// Modules that this module uses.
|
||||
List<Module> get upstream;
|
||||
|
||||
/// The module's variables.
|
||||
Map<String, Value> get variables;
|
||||
|
||||
|
@ -242,7 +242,7 @@ class _EvaluateVisitor
|
||||
var module = await _execute(importer, node);
|
||||
_extender.finalize();
|
||||
|
||||
return EvaluateResult(module.css, _includedFiles);
|
||||
return EvaluateResult(_combineCss(module), _includedFiles);
|
||||
}
|
||||
|
||||
Future<Value> runExpression(Expression expression,
|
||||
@ -406,6 +406,47 @@ class _EvaluateVisitor
|
||||
return CssStylesheet(statements.build(), _root.span);
|
||||
}
|
||||
|
||||
/// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all
|
||||
/// modules transitively used by [root].
|
||||
CssStylesheet _combineCss(AsyncModule root) {
|
||||
if (root.upstream.isEmpty) return root.css;
|
||||
|
||||
var seen = Set<AsyncModule>();
|
||||
var imports = <CssNode>[];
|
||||
var css = <CssNode>[];
|
||||
|
||||
void visitModule(AsyncModule module) {
|
||||
if (!seen.add(module)) return;
|
||||
for (var module in module.upstream) {
|
||||
visitModule(module);
|
||||
}
|
||||
|
||||
var statements = module.css.children;
|
||||
var index = _indexAfterImports(statements);
|
||||
imports.addAll(statements.getRange(0, index));
|
||||
css.addAll(statements.getRange(index, statements.length));
|
||||
}
|
||||
|
||||
visitModule(root);
|
||||
|
||||
return CssStylesheet(imports + css, root.css.span);
|
||||
}
|
||||
|
||||
/// Returns the index of the first node in [statements] that comes after all
|
||||
/// static imports.
|
||||
int _indexAfterImports(List<CssNode> statements) {
|
||||
var lastImport = -1;
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var statement = statements[i];
|
||||
if (statement is CssImport) {
|
||||
lastImport = i;
|
||||
} else if (statement is! CssComment) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lastImport + 1;
|
||||
}
|
||||
|
||||
// ## Statements
|
||||
|
||||
Future<Value> visitStylesheet(Stylesheet node) async {
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: ae0454752ee85a9094d0f0d0e6e8a252364b70a1
|
||||
// Checksum: da6068544fd58007ee2d44c2688063cbe4543e73
|
||||
//
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
@ -249,7 +249,7 @@ class _EvaluateVisitor
|
||||
var module = _execute(importer, node);
|
||||
_extender.finalize();
|
||||
|
||||
return EvaluateResult(module.css, _includedFiles);
|
||||
return EvaluateResult(_combineCss(module), _includedFiles);
|
||||
}
|
||||
|
||||
Value runExpression(Expression expression, {Map<String, Value> variables}) {
|
||||
@ -412,6 +412,47 @@ class _EvaluateVisitor
|
||||
return CssStylesheet(statements.build(), _root.span);
|
||||
}
|
||||
|
||||
/// Returns a new stylesheet containing [root]'s CSS as well as the CSS of all
|
||||
/// modules transitively used by [root].
|
||||
CssStylesheet _combineCss(Module root) {
|
||||
if (root.upstream.isEmpty) return root.css;
|
||||
|
||||
var seen = Set<Module>();
|
||||
var imports = <CssNode>[];
|
||||
var css = <CssNode>[];
|
||||
|
||||
void visitModule(Module module) {
|
||||
if (!seen.add(module)) return;
|
||||
for (var module in module.upstream) {
|
||||
visitModule(module);
|
||||
}
|
||||
|
||||
var statements = module.css.children;
|
||||
var index = _indexAfterImports(statements);
|
||||
imports.addAll(statements.getRange(0, index));
|
||||
css.addAll(statements.getRange(index, statements.length));
|
||||
}
|
||||
|
||||
visitModule(root);
|
||||
|
||||
return CssStylesheet(imports + css, root.css.span);
|
||||
}
|
||||
|
||||
/// Returns the index of the first node in [statements] that comes after all
|
||||
/// static imports.
|
||||
int _indexAfterImports(List<CssNode> statements) {
|
||||
var lastImport = -1;
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var statement = statements[i];
|
||||
if (statement is CssImport) {
|
||||
lastImport = i;
|
||||
} else if (statement is! CssComment) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lastImport + 1;
|
||||
}
|
||||
|
||||
// ## Statements
|
||||
|
||||
Value visitStylesheet(Stylesheet node) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user