mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-02 09:37:49 +01:00
Finish API docs.
This commit is contained in:
parent
38f15a96f3
commit
d276bfb206
@ -145,13 +145,23 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
|
||||
if (outerCopy != null) root.addChild(outerCopy);
|
||||
_scopeForAtRule(innerCopy ?? root, query)(() {
|
||||
_scopeForAtRoot(innerCopy ?? root, query)(() {
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Destructively trims a trailing sublist that matches the current list of
|
||||
/// parents from [nodes].
|
||||
///
|
||||
/// [nodes] should be a list of parents included by an `@at-root` rule, from
|
||||
/// innermost to outermost. If it contains a trailing sublist that's
|
||||
/// contiguous—meaning that each node is a direct parent of the node before
|
||||
/// it—and whose final node is a direct child of [_root], this removes that
|
||||
/// sublist and returns the innermost removed parent.
|
||||
///
|
||||
/// Otherwise, this leaves [nodes] as-is and returns [_root].
|
||||
CssParentNode _trimIncluded(List<CssParentNode> nodes) {
|
||||
var parent = _parent;
|
||||
int innermostContiguous;
|
||||
@ -171,7 +181,12 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return root;
|
||||
}
|
||||
|
||||
_ScopeCallback _scopeForAtRule(CssNode newParent, AtRootQuery query) {
|
||||
/// Returns a [_ScopeCallback] for [query].
|
||||
///
|
||||
/// This returns a callback that adjusts various instance variables for its
|
||||
/// duration, based on which rules are excluded by [query]. It always assigns
|
||||
/// [_parent] to [newParent].
|
||||
_ScopeCallback _scopeForAtRoot(CssNode newParent, AtRootQuery query) {
|
||||
var scope = (callback()) {
|
||||
// We can't use [_withParent] here because it'll add the node to the tree
|
||||
// in the wrong place.
|
||||
@ -260,6 +275,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}, semiGlobal: true);
|
||||
}
|
||||
|
||||
/// Destructures [value] and assigns it to [variables], as in an `@each`
|
||||
/// statement.
|
||||
void _setMultipleVariables(List<String> variables, Value value) {
|
||||
var list = value.asList;
|
||||
var minLength = math.min(variables.length, list.length);
|
||||
@ -377,6 +394,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Loads the [Stylesheet] imported by [node], or throws a
|
||||
/// [SassRuntimeException] if loading fails.
|
||||
Stylesheet _loadImport(ImportRule node) {
|
||||
var path = _importPaths.putIfAbsent(node, () {
|
||||
var path = p.fromUri(node.url);
|
||||
@ -408,9 +427,14 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Like [_tryImportPath], but checks both `.sass` and `.scss` extensions.
|
||||
String _tryImportPathWithExtensions(String path) =>
|
||||
_tryImportPath(path + '.sass') ?? _tryImportPath(path + '.scss');
|
||||
|
||||
/// If a file exists at [path], or a partial with the same name exists,
|
||||
/// returns the resolved path.
|
||||
///
|
||||
/// Otherwise, returns `null`.
|
||||
String _tryImportPath(String path) {
|
||||
var partial = p.join(p.dirname(path), "_${p.basename(path)}");
|
||||
if (fileExists(partial)) return partial;
|
||||
@ -484,6 +508,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}, through: (node) => node is CssStyleRule || node is CssMediaRule);
|
||||
}
|
||||
|
||||
/// Returns a list of queries that selects for platforms that match both
|
||||
/// [queries1] and [queries2].
|
||||
List<CssMediaQuery> _mergeMediaQueries(
|
||||
Iterable<CssMediaQuery> queries1, Iterable<CssMediaQuery> queries2) {
|
||||
return new List.unmodifiable(queries1.expand/*<CssMediaQuery>*/((query1) {
|
||||
@ -491,6 +517,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}).where((query) => query != null));
|
||||
}
|
||||
|
||||
/// Evaluates [query] and converts it to a plain CSS query.
|
||||
CssMediaQuery _visitMediaQuery(MediaQuery query) {
|
||||
var modifier =
|
||||
query.modifier == null ? null : _interpolationToValue(query.modifier);
|
||||
@ -564,6 +591,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}, through: (node) => node is CssStyleRule);
|
||||
}
|
||||
|
||||
/// Evaluates [condition] and converts it to a plain CSS string.
|
||||
String _visitSupportsCondition(SupportsCondition condition) {
|
||||
if (condition is SupportsOperation) {
|
||||
return "${_parenthesize(condition.left, condition.operator)} "
|
||||
@ -581,6 +609,12 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evlauates [condition] and converts it to a plain CSS string, with
|
||||
/// parentheses if necessary.
|
||||
///
|
||||
/// If [operator] is passed, it's the operator for the surrounding
|
||||
/// [SupportsOperation], and is used to determine whether parentheses are
|
||||
/// necessary if [condition] is also a [SupportsOperation].
|
||||
String _parenthesize(SupportsCondition condition, [String operator]) {
|
||||
if ((condition is SupportsNegation) ||
|
||||
(condition is SupportsOperation &&
|
||||
@ -760,6 +794,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return new SassString("$name(${arguments.map(valueToCss).join(', ')})");
|
||||
}
|
||||
|
||||
/// Evaluates the arguments in [invocation] as applied to [callable], and
|
||||
/// invokes [run] in a scope with those arguments defined.
|
||||
Value _runUserDefinedCallable(CallableInvocation invocation,
|
||||
UserDefinedCallable callable, Value run()) {
|
||||
var triple = _evaluateArguments(invocation);
|
||||
@ -819,6 +855,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Evaluates [invocation] as applied to [callable], and invokes [callable]'s
|
||||
/// body.
|
||||
Value _runBuiltInCallable(
|
||||
CallableInvocation invocation, BuiltInCallable callable) {
|
||||
var triple = _evaluateArguments(invocation);
|
||||
@ -881,6 +919,9 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
invocation.span);
|
||||
}
|
||||
|
||||
/// Evaluates the arguments in [invocation] and returns the positional and
|
||||
/// named arguments, as well as the [ListSeparator] for the rest argument
|
||||
/// list, if any.
|
||||
Tuple3<List<Value>, Map<String, Value>, ListSeparator> _evaluateArguments(
|
||||
CallableInvocation invocation) {
|
||||
var positional = invocation.arguments.positional
|
||||
@ -925,6 +966,11 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the arguments in [invocation] only as much as necessary to
|
||||
/// separate out positional and named arguments.
|
||||
///
|
||||
/// Returns the arguments as expressions so that they can be lazily evaluated
|
||||
/// for macros such as `if()`.
|
||||
Tuple2<List<Expression>, Map<String, Expression>> _evaluateMacroArguments(
|
||||
CallableInvocation invocation) {
|
||||
if (invocation.arguments.rest == null) {
|
||||
@ -966,6 +1012,13 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the values in [map] to [values].
|
||||
///
|
||||
/// Throws a [SassRuntimeException] associated with [span] if any [map] keys
|
||||
/// aren't strings.
|
||||
///
|
||||
/// If [convert] is passed, that's used to convert the map values to the value
|
||||
/// type for [values]. Otherwise, the [Value]s are used as-is.
|
||||
void _addRestMap/*<T>*/(
|
||||
Map<String, Object/*=T*/ > values, SassMap map, FileSpan span,
|
||||
[/*=T*/ convert(Value value)]) {
|
||||
@ -982,6 +1035,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Throws a [SassRuntimeException] if [positional] and [named] aren't valid
|
||||
/// when applied to [arguments].
|
||||
void _verifyArguments(int positional, Map<String, dynamic> named,
|
||||
ArgumentDeclaration arguments, FileSpan span) {
|
||||
for (var i = 0; i < arguments.arguments.length; i++) {
|
||||
@ -1031,6 +1086,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
|
||||
// ## Utilities
|
||||
|
||||
/// Runs [callback] with [environment] as the current environment.
|
||||
/*=T*/ _withEnvironment/*<T>*/(Environment environment, /*=T*/ callback()) {
|
||||
var oldEnvironment = _environment;
|
||||
_environment = environment;
|
||||
@ -1039,12 +1095,16 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Evaluates [interpolation] and wraps the result in a [CssValue].
|
||||
///
|
||||
/// If [trim] is `true`, removes whitespace around the result.
|
||||
CssValue<String> _interpolationToValue(Interpolation interpolation,
|
||||
{bool trim: false}) {
|
||||
var result = _performInterpolation(interpolation);
|
||||
return new CssValue(trim ? result.trim() : result, interpolation.span);
|
||||
}
|
||||
|
||||
/// Evaluates [interpolation].
|
||||
String _performInterpolation(Interpolation interpolation) {
|
||||
return interpolation.contents.map((value) {
|
||||
if (value is String) return value;
|
||||
@ -1053,9 +1113,15 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}).join();
|
||||
}
|
||||
|
||||
/// Evaluates [expression] and wraps the result in a [CssValue].
|
||||
CssValue<Value> _performExpression(Expression expression) =>
|
||||
new CssValue(expression.accept(this), expression.span);
|
||||
|
||||
/// Adds [node] as a child of the current parent, then runs [callback] with
|
||||
/// [node] as the current parent.
|
||||
///
|
||||
/// If [through] is passed, [node] is added as a child of the first parent for
|
||||
/// which [through] returns `false`.
|
||||
/*=T*/ _withParent/*<S extends CssParentNode, T>*/(
|
||||
/*=S*/ node,
|
||||
/*=T*/ callback(),
|
||||
@ -1078,6 +1144,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Runs [callback] with [selector] as the current selector.
|
||||
/*=T*/ _withSelector/*<T>*/(
|
||||
CssValue<SelectorList> selector,
|
||||
/*=T*/ callback()) {
|
||||
@ -1088,6 +1155,7 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Runs [callback] with [queries] as the current media queries.
|
||||
/*=T*/ _withMediaQueries/*<T>*/(
|
||||
List<CssMediaQuery> queries,
|
||||
/*=T*/ callback()) {
|
||||
@ -1098,6 +1166,10 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Adds a frame to the stack with the given [member] name, and [span] as the
|
||||
/// site of the new frame.
|
||||
///
|
||||
/// Runs [callback] with the new stack.
|
||||
/*=T*/ _withStackFrame/*<T>*/(
|
||||
String member, FileSpan span, /*=T*/ callback()) {
|
||||
_stack.add(_stackFrame(span));
|
||||
@ -1109,17 +1181,29 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a new stack frame with location information from [span] and
|
||||
/// [_member].
|
||||
Frame _stackFrame(FileSpan span) => new Frame(
|
||||
span.sourceUrl, span.start.line + 1, span.start.column + 1, _member);
|
||||
|
||||
/// Returns a stack trace at the current point.
|
||||
///
|
||||
/// [span] is the current location, used for the bottom-most stack frame.
|
||||
Trace _stackTrace(FileSpan span) {
|
||||
var frames = _stack.toList()..add(_stackFrame(span));
|
||||
return new Trace(frames.reversed);
|
||||
}
|
||||
|
||||
/// Throws a [SassRuntimeException] with the given [message] and [span].
|
||||
SassRuntimeException _exception(String message, FileSpan span) =>
|
||||
new SassRuntimeException(message, span, _stackTrace(span));
|
||||
|
||||
/// Runs [callback], and adjusts any [SassFormatException] to be within [span].
|
||||
///
|
||||
/// Specifically, this adjusts format exceptions so that the errors are
|
||||
/// reported as though the text being parsed were exactly in [span]. This may
|
||||
/// not be quite accurate if the source text contained interpolation, but
|
||||
/// it'll still produce a useful error.
|
||||
/*=T*/ _adjustParseError/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
||||
try {
|
||||
return callback();
|
||||
@ -1135,6 +1219,8 @@ class _PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs [callback], and converts any [InternalException]s it throws to
|
||||
/// [SassRuntimeException]s with [span].
|
||||
/*=T*/ _addExceptionSpan/*<T>*/(FileSpan span, /*=T*/ callback()) {
|
||||
try {
|
||||
return callback();
|
||||
|
@ -16,6 +16,15 @@ import 'interface/css.dart';
|
||||
import 'interface/selector.dart';
|
||||
import 'interface/value.dart';
|
||||
|
||||
/// Converts [node] to a CSS string.
|
||||
///
|
||||
/// If [style] is passed, it controls the style of the resulting CSS. It
|
||||
/// defaults to [OutputStyle.expanded].
|
||||
///
|
||||
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||
/// source structure. Note however that, although this will be valid SCSS, it
|
||||
/// may not be valid CSS. If [inspect] is `false` and [node] contains any values
|
||||
/// that can't be represented in plain CSS, throws a [SassException].
|
||||
String toCss(CssNode node, {OutputStyle style, bool inspect: false}) {
|
||||
var visitor = new _SerializeCssVisitor(style: style, inspect: inspect);
|
||||
node.accept(visitor);
|
||||
@ -29,26 +38,41 @@ String toCss(CssNode node, {OutputStyle style, bool inspect: false}) {
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
// Note: this may throw an [InternalException] if [inspect] is `false`.
|
||||
/// Converts [value] to a CSS string.
|
||||
///
|
||||
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||
/// source structure. Note however that, although this will be valid SCSS, it
|
||||
/// may not be valid CSS. If [inspect] is `false` and [value] can't be
|
||||
/// represented in plain CSS, throws an [InternalException].
|
||||
String valueToCss(Value value, {bool inspect: false}) {
|
||||
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
||||
value.accept(visitor);
|
||||
return visitor._buffer.toString();
|
||||
}
|
||||
|
||||
// Note: this may throw an [InternalException] if [inspect] is `false`.
|
||||
/// Converts [selector] to a CSS string.
|
||||
///
|
||||
/// If [inspect] is `true`, this will emit an unambiguous representation of the
|
||||
/// source structure. Note however that, although this will be valid SCSS, it
|
||||
/// may not be valid CSS. If [inspect] is `false` and [selector] can't be
|
||||
/// represented in plain CSS, throws an [InternalException].
|
||||
String selectorToCss(Selector selector, {bool inspect: false}) {
|
||||
var visitor = new _SerializeCssVisitor(inspect: inspect);
|
||||
selector.accept(visitor);
|
||||
return visitor._buffer.toString();
|
||||
}
|
||||
|
||||
/// A visitor that converts CSS syntax trees to plain strings.
|
||||
class _SerializeCssVisitor
|
||||
implements CssVisitor, ValueVisitor, SelectorVisitor {
|
||||
/// A buffer that contains the CSS produced so far.
|
||||
final _buffer = new StringBuffer();
|
||||
|
||||
/// The current indentation of the CSS output.
|
||||
var _indentation = 0;
|
||||
|
||||
/// Whether we're emitting an unambiguous representation of the source
|
||||
/// structure, as opposed to valid CSS.
|
||||
final bool _inspect;
|
||||
|
||||
_SerializeCssVisitor({OutputStyle style, bool inspect: false})
|
||||
@ -162,6 +186,9 @@ class _SerializeCssVisitor
|
||||
_buffer.writeCharCode($semicolon);
|
||||
}
|
||||
|
||||
/// Emits the value of [node] as a custom property value.
|
||||
///
|
||||
/// This re-indents [node]'s value relative to the current indentation.
|
||||
void _writeCustomPropertyValue(CssDeclaration node) {
|
||||
var value = (node.value.value as SassString).text;
|
||||
|
||||
@ -179,6 +206,8 @@ class _SerializeCssVisitor
|
||||
_writeWithIndent(value, minimumIndentation);
|
||||
}
|
||||
|
||||
/// Returns the indentation level of the least-indented, non-empty line in
|
||||
/// [text].
|
||||
int _minimumIndentation(String text) {
|
||||
var scanner = new LineScanner(text);
|
||||
while (!scanner.isDone && scanner.readChar() != $lf) {}
|
||||
@ -195,6 +224,8 @@ class _SerializeCssVisitor
|
||||
return min;
|
||||
}
|
||||
|
||||
/// Writes [text] to [_buffer], adding [minimumIndentation] to each non-empty
|
||||
/// line.
|
||||
void _writeWithIndent(String text, int minimumIndentation) {
|
||||
var scanner = new LineScanner(text);
|
||||
while (!scanner.isDone && scanner.peekChar() != $lf) {
|
||||
@ -213,6 +244,8 @@ class _SerializeCssVisitor
|
||||
|
||||
// ## Values
|
||||
|
||||
/// Converts [value] to a plain CSS string, converting any
|
||||
/// [InternalException]s to [SassException]s.
|
||||
void _visitValue(CssValue<Value> value) {
|
||||
try {
|
||||
value.value.accept(this);
|
||||
@ -238,7 +271,9 @@ class _SerializeCssVisitor
|
||||
}
|
||||
}
|
||||
|
||||
/// Emits [color] as a hex character pair.
|
||||
void _writeHexComponent(int color) {
|
||||
assert(color < 0x100);
|
||||
_buffer.writeCharCode(hexCharFor(color >> 4));
|
||||
_buffer.writeCharCode(hexCharFor(color & 0xF));
|
||||
}
|
||||
@ -267,6 +302,8 @@ class _SerializeCssVisitor
|
||||
if (value.hasBrackets) _buffer.writeCharCode($rbracket);
|
||||
}
|
||||
|
||||
/// Returns whether [value] needs parentheses as an element in a list with the
|
||||
/// given [separator].
|
||||
bool _elementNeedsParens(ListSeparator separator, Value value) {
|
||||
if (value is SassList) {
|
||||
if (value.contents.length < 2) return false;
|
||||
@ -289,6 +326,7 @@ class _SerializeCssVisitor
|
||||
_buffer.writeCharCode($rparen);
|
||||
}
|
||||
|
||||
/// Writes [value] as key or value in a map, with parentheses as necessary.
|
||||
void _writeMapElement(Value value) {
|
||||
var needsParens = value is SassList &&
|
||||
value.separator == ListSeparator.comma &&
|
||||
@ -328,6 +366,11 @@ class _SerializeCssVisitor
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes [string] to [_buffer].
|
||||
///
|
||||
/// By default, this detects which type of quote to use based on the contents
|
||||
/// of the string. If [forceDoubleQuote] is `true`, this always uses a double
|
||||
/// quote.
|
||||
void _visitString(String string, {bool forceDoubleQuote: false}) {
|
||||
var includesSingleQuote = false;
|
||||
var includesDoubleQuote = false;
|
||||
@ -491,6 +534,7 @@ class _SerializeCssVisitor
|
||||
|
||||
// ## Utilities
|
||||
|
||||
/// Emits [children] in a block.
|
||||
void _visitChildren(Iterable<CssNode> children) {
|
||||
_buffer.writeCharCode($lbrace);
|
||||
if (children.every(_isInvisible)) {
|
||||
@ -510,6 +554,7 @@ class _SerializeCssVisitor
|
||||
_buffer.writeCharCode($rbrace);
|
||||
}
|
||||
|
||||
/// Writes indentation based on [_indentation].
|
||||
void _writeIndentation() {
|
||||
for (var i = 0; i < _indentation; i++) {
|
||||
_buffer.writeCharCode($space);
|
||||
@ -517,6 +562,8 @@ class _SerializeCssVisitor
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls [callback] to write each value in [iterable], and writes [text] in
|
||||
/// between each one.
|
||||
void _writeBetween/*<T>*/(
|
||||
Iterable/*<T>*/ iterable, String text, void callback(/*=T*/ value)) {
|
||||
var first = true;
|
||||
@ -530,19 +577,23 @@ class _SerializeCssVisitor
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs [callback] with indentation increased one level.
|
||||
void _indent(void callback()) {
|
||||
_indentation++;
|
||||
callback();
|
||||
_indentation--;
|
||||
}
|
||||
|
||||
/// Returns whether [node] is considered invisible.
|
||||
bool _isInvisible(CssNode node) => !_inspect && node.isInvisible;
|
||||
}
|
||||
|
||||
/// An enum of generated CSS styles.
|
||||
class OutputStyle {
|
||||
/// The standard CSS style, with each declaration on its own line.
|
||||
static const expanded = const OutputStyle._("expanded");
|
||||
static const nested = const OutputStyle._("nested");
|
||||
|
||||
/// The name of the style.
|
||||
final String _name;
|
||||
|
||||
const OutputStyle._(this._name);
|
||||
|
Loading…
Reference in New Issue
Block a user