mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-02 09:37:49 +01:00
commit
435e1b2f71
@ -1,3 +1,7 @@
|
|||||||
|
## 1.43.3
|
||||||
|
|
||||||
|
* Improve performance.
|
||||||
|
|
||||||
## 1.43.2
|
## 1.43.2
|
||||||
|
|
||||||
* Improve the error message when the default namespace of a `@use` rule is not
|
* Improve the error message when the default namespace of a `@use` rule is not
|
||||||
|
@ -28,7 +28,7 @@ export 'src/logger.dart';
|
|||||||
export 'src/syntax.dart';
|
export 'src/syntax.dart';
|
||||||
export 'src/value.dart' hide SassApiColor;
|
export 'src/value.dart' hide SassApiColor;
|
||||||
export 'src/visitor/serialize.dart' show OutputStyle;
|
export 'src/visitor/serialize.dart' show OutputStyle;
|
||||||
export 'src/warn.dart' show warn;
|
export 'src/evaluation_context.dart' show warn;
|
||||||
|
|
||||||
/// Loads the Sass file at [path], compiles it to CSS, and returns a
|
/// Loads the Sass file at [path], compiles it to CSS, and returns a
|
||||||
/// [CompileResult] containing the CSS and additional metadata about the
|
/// [CompileResult] containing the CSS and additional metadata about the
|
||||||
|
@ -33,16 +33,31 @@ class AsyncImportCache {
|
|||||||
///
|
///
|
||||||
/// This map's values are the same as the return value of [canonicalize].
|
/// This map's values are the same as the return value of [canonicalize].
|
||||||
///
|
///
|
||||||
/// This cache isn't used for relative imports, because they're
|
/// This cache isn't used for relative imports, because they depend on the
|
||||||
/// context-dependent.
|
/// specific base importer. That's stored separately in
|
||||||
final Map<Tuple2<Uri, bool>, Tuple3<AsyncImporter, Uri, Uri>?>
|
/// [_relativeCanonicalizeCache].
|
||||||
_canonicalizeCache;
|
final _canonicalizeCache =
|
||||||
|
<Tuple2<Uri, bool>, Tuple3<AsyncImporter, Uri, Uri>?>{};
|
||||||
|
|
||||||
|
/// The canonicalized URLs for each non-canonical URL that's resolved using a
|
||||||
|
/// relative importer.
|
||||||
|
///
|
||||||
|
/// The map's keys have four parts:
|
||||||
|
///
|
||||||
|
/// 1. The URL passed to [canonicalize] (the same as in [_canonicalizeCache]).
|
||||||
|
/// 2. Whether the canonicalization is for an `@import` rule.
|
||||||
|
/// 3. The `baseImporter` passed to [canonicalize].
|
||||||
|
/// 4. The `baseUrl` passed to [canonicalize].
|
||||||
|
///
|
||||||
|
/// The map's values are the same as the return value of [canonicalize].
|
||||||
|
final _relativeCanonicalizeCache = <Tuple4<Uri, bool, AsyncImporter, Uri?>,
|
||||||
|
Tuple3<AsyncImporter, Uri, Uri>?>{};
|
||||||
|
|
||||||
/// The parsed stylesheets for each canonicalized import URL.
|
/// The parsed stylesheets for each canonicalized import URL.
|
||||||
final Map<Uri, Stylesheet?> _importCache;
|
final _importCache = <Uri, Stylesheet?>{};
|
||||||
|
|
||||||
/// The import results for each canonicalized import URL.
|
/// The import results for each canonicalized import URL.
|
||||||
final Map<Uri, ImporterResult> _resultsCache;
|
final _resultsCache = <Uri, ImporterResult>{};
|
||||||
|
|
||||||
/// Creates an import cache that resolves imports using [importers].
|
/// Creates an import cache that resolves imports using [importers].
|
||||||
///
|
///
|
||||||
@ -67,18 +82,12 @@ class AsyncImportCache {
|
|||||||
PackageConfig? packageConfig,
|
PackageConfig? packageConfig,
|
||||||
Logger? logger})
|
Logger? logger})
|
||||||
: _importers = _toImporters(importers, loadPaths, packageConfig),
|
: _importers = _toImporters(importers, loadPaths, packageConfig),
|
||||||
_logger = logger ?? const Logger.stderr(),
|
_logger = logger ?? const Logger.stderr();
|
||||||
_canonicalizeCache = {},
|
|
||||||
_importCache = {},
|
|
||||||
_resultsCache = {};
|
|
||||||
|
|
||||||
/// Creates an import cache without any globally-available importers.
|
/// Creates an import cache without any globally-available importers.
|
||||||
AsyncImportCache.none({Logger? logger})
|
AsyncImportCache.none({Logger? logger})
|
||||||
: _importers = const [],
|
: _importers = const [],
|
||||||
_logger = logger ?? const Logger.stderr(),
|
_logger = logger ?? const Logger.stderr();
|
||||||
_canonicalizeCache = {},
|
|
||||||
_importCache = {},
|
|
||||||
_resultsCache = {};
|
|
||||||
|
|
||||||
/// Converts the user's [importers], [loadPaths], and [packageConfig]
|
/// Converts the user's [importers], [loadPaths], and [packageConfig]
|
||||||
/// options into a single list of importers.
|
/// options into a single list of importers.
|
||||||
@ -113,12 +122,16 @@ class AsyncImportCache {
|
|||||||
Uri? baseUrl,
|
Uri? baseUrl,
|
||||||
bool forImport = false}) async {
|
bool forImport = false}) async {
|
||||||
if (baseImporter != null) {
|
if (baseImporter != null) {
|
||||||
var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
|
var relativeResult = await putIfAbsentAsync(_relativeCanonicalizeCache,
|
||||||
var canonicalUrl =
|
Tuple4(url, forImport, baseImporter, baseUrl), () async {
|
||||||
await _canonicalize(baseImporter, resolvedUrl, forImport);
|
var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
|
||||||
if (canonicalUrl != null) {
|
var canonicalUrl =
|
||||||
return Tuple3(baseImporter, canonicalUrl, resolvedUrl);
|
await _canonicalize(baseImporter, resolvedUrl, forImport);
|
||||||
}
|
if (canonicalUrl != null) {
|
||||||
|
return Tuple3(baseImporter, canonicalUrl, resolvedUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (relativeResult != null) return relativeResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await putIfAbsentAsync(_canonicalizeCache, Tuple2(url, forImport),
|
return await putIfAbsentAsync(_canonicalizeCache, Tuple2(url, forImport),
|
||||||
@ -236,6 +249,14 @@ Relative canonical URLs are deprecated and will eventually be disallowed.
|
|||||||
void clearCanonicalize(Uri url) {
|
void clearCanonicalize(Uri url) {
|
||||||
_canonicalizeCache.remove(Tuple2(url, false));
|
_canonicalizeCache.remove(Tuple2(url, false));
|
||||||
_canonicalizeCache.remove(Tuple2(url, true));
|
_canonicalizeCache.remove(Tuple2(url, true));
|
||||||
|
|
||||||
|
var relativeKeysToClear = [
|
||||||
|
for (var key in _relativeCanonicalizeCache.keys)
|
||||||
|
if (key.item1 == url) key
|
||||||
|
];
|
||||||
|
for (var key in relativeKeysToClear) {
|
||||||
|
_relativeCanonicalizeCache.remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the cached parse tree for the stylesheet with the given
|
/// Clears the cached parse tree for the stylesheet with the given
|
||||||
|
57
lib/src/evaluation_context.dart
Normal file
57
lib/src/evaluation_context.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright 2021 Google Inc. Use of this source code is governed by an
|
||||||
|
// MIT-style license that can be found in the LICENSE file or at
|
||||||
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:source_span/source_span.dart';
|
||||||
|
|
||||||
|
/// An interface that exposes information about the current Sass evaluation.
|
||||||
|
///
|
||||||
|
/// This allows us to expose zone-scoped information without having to create a
|
||||||
|
/// new zone variable for each piece of information.
|
||||||
|
abstract class EvaluationContext {
|
||||||
|
/// The current evaluation context.
|
||||||
|
///
|
||||||
|
/// Throws [StateError] if there isn't a Sass stylesheet currently being
|
||||||
|
/// evaluated.
|
||||||
|
static EvaluationContext get current {
|
||||||
|
var context = Zone.current[#_evaluationContext];
|
||||||
|
if (context is EvaluationContext) return context;
|
||||||
|
throw StateError("No Sass stylesheet is currently being evaluated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the span for the currently executing callable.
|
||||||
|
///
|
||||||
|
/// For normal exception reporting, this should be avoided in favor of
|
||||||
|
/// throwing [SassScriptException]s. It should only be used when calling APIs
|
||||||
|
/// that require spans.
|
||||||
|
///
|
||||||
|
/// Throws a [StateError] if there isn't a callable being invoked.
|
||||||
|
FileSpan get currentCallableSpan;
|
||||||
|
|
||||||
|
/// Prints a warning message associated with the current `@import` or function
|
||||||
|
/// call.
|
||||||
|
///
|
||||||
|
/// If [deprecation] is `true`, the warning is emitted as a deprecation
|
||||||
|
/// warning.
|
||||||
|
void warn(String message, {bool deprecation = false});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints a warning message associated with the current `@import` or function
|
||||||
|
/// call.
|
||||||
|
///
|
||||||
|
/// If [deprecation] is `true`, the warning is emitted as a deprecation warning.
|
||||||
|
///
|
||||||
|
/// This may only be called within a custom function or importer callback.
|
||||||
|
///
|
||||||
|
/// {@category Compile}
|
||||||
|
void warn(String message, {bool deprecation = false}) =>
|
||||||
|
EvaluationContext.current.warn(message, deprecation: deprecation);
|
||||||
|
|
||||||
|
/// Runs [callback] with [context] as [EvaluationContext.current].
|
||||||
|
///
|
||||||
|
/// This is zone-based, so if [callback] is asynchronous [warn] is set for the
|
||||||
|
/// duration of that callback.
|
||||||
|
T withEvaluationContext<T>(EvaluationContext context, T callback()) =>
|
||||||
|
runZoned(callback, zoneValues: {#_evaluationContext: context});
|
@ -3,12 +3,9 @@
|
|||||||
// https://opensource.org/licenses/MIT.
|
// https://opensource.org/licenses/MIT.
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:source_span/source_span.dart';
|
|
||||||
|
|
||||||
import 'ast/node.dart';
|
|
||||||
import 'callable.dart';
|
import 'callable.dart';
|
||||||
import 'functions/color.dart' as color;
|
import 'functions/color.dart' as color;
|
||||||
import 'functions/list.dart' as list;
|
import 'functions/list.dart' as list;
|
||||||
@ -49,21 +46,3 @@ final coreModules = UnmodifiableListView([
|
|||||||
selector.module,
|
selector.module,
|
||||||
string.module
|
string.module
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Returns the span for the currently executing callable.
|
|
||||||
///
|
|
||||||
/// For normal exception reporting, this should be avoided in favor of throwing
|
|
||||||
/// [SassScriptException]s. It should only be used when calling APIs that
|
|
||||||
/// require spans.
|
|
||||||
FileSpan get currentCallableSpan {
|
|
||||||
var node = Zone.current[#_currentCallableNode];
|
|
||||||
if (node is AstNode) return node.span;
|
|
||||||
|
|
||||||
throw StateError("currentCallableSpan may only be called within an "
|
|
||||||
"active Sass callable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs [callback] in a zone with [callableNode]'s span available from
|
|
||||||
/// [currentCallableSpan].
|
|
||||||
T withCurrentCallableNode<T>(AstNode callableNode, T callback()) =>
|
|
||||||
runZoned(callback, zoneValues: {#_currentCallableNode: callableNode});
|
|
||||||
|
@ -7,13 +7,13 @@ import 'dart:collection';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import '../callable.dart';
|
import '../callable.dart';
|
||||||
|
import '../evaluation_context.dart';
|
||||||
import '../exception.dart';
|
import '../exception.dart';
|
||||||
import '../module/built_in.dart';
|
import '../module/built_in.dart';
|
||||||
import '../util/number.dart';
|
import '../util/number.dart';
|
||||||
import '../util/nullable.dart';
|
import '../util/nullable.dart';
|
||||||
import '../utils.dart';
|
import '../utils.dart';
|
||||||
import '../value.dart';
|
import '../value.dart';
|
||||||
import '../warn.dart';
|
|
||||||
|
|
||||||
/// A regular expression matching the beginning of a proprietary Microsoft
|
/// A regular expression matching the beginning of a proprietary Microsoft
|
||||||
/// filter declaration.
|
/// filter declaration.
|
||||||
|
@ -8,11 +8,11 @@ import 'dart:math' as math;
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import '../callable.dart';
|
import '../callable.dart';
|
||||||
|
import '../evaluation_context.dart';
|
||||||
import '../exception.dart';
|
import '../exception.dart';
|
||||||
import '../module/built_in.dart';
|
import '../module/built_in.dart';
|
||||||
import '../util/number.dart';
|
import '../util/number.dart';
|
||||||
import '../value.dart';
|
import '../value.dart';
|
||||||
import '../warn.dart';
|
|
||||||
|
|
||||||
/// The global definitions of Sass math functions.
|
/// The global definitions of Sass math functions.
|
||||||
final global = UnmodifiableListView([
|
final global = UnmodifiableListView([
|
||||||
|
@ -8,9 +8,9 @@ import 'package:collection/collection.dart';
|
|||||||
|
|
||||||
import '../ast/selector.dart';
|
import '../ast/selector.dart';
|
||||||
import '../callable.dart';
|
import '../callable.dart';
|
||||||
|
import '../evaluation_context.dart';
|
||||||
import '../exception.dart';
|
import '../exception.dart';
|
||||||
import '../extend/extension_store.dart';
|
import '../extend/extension_store.dart';
|
||||||
import '../functions.dart';
|
|
||||||
import '../module/built_in.dart';
|
import '../module/built_in.dart';
|
||||||
import '../value.dart';
|
import '../value.dart';
|
||||||
|
|
||||||
@ -88,7 +88,8 @@ final _extend =
|
|||||||
var target = arguments[1].assertSelector(name: "extendee");
|
var target = arguments[1].assertSelector(name: "extendee");
|
||||||
var source = arguments[2].assertSelector(name: "extender");
|
var source = arguments[2].assertSelector(name: "extender");
|
||||||
|
|
||||||
return ExtensionStore.extend(selector, source, target, currentCallableSpan)
|
return ExtensionStore.extend(selector, source, target,
|
||||||
|
EvaluationContext.current.currentCallableSpan)
|
||||||
.asSassList;
|
.asSassList;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,7 +99,8 @@ final _replace =
|
|||||||
var target = arguments[1].assertSelector(name: "original");
|
var target = arguments[1].assertSelector(name: "original");
|
||||||
var source = arguments[2].assertSelector(name: "replacement");
|
var source = arguments[2].assertSelector(name: "replacement");
|
||||||
|
|
||||||
return ExtensionStore.replace(selector, source, target, currentCallableSpan)
|
return ExtensionStore.replace(selector, source, target,
|
||||||
|
EvaluationContext.current.currentCallableSpan)
|
||||||
.asSassList;
|
.asSassList;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// DO NOT EDIT. This file was generated from async_import_cache.dart.
|
// DO NOT EDIT. This file was generated from async_import_cache.dart.
|
||||||
// See tool/grind/synchronize.dart for details.
|
// See tool/grind/synchronize.dart for details.
|
||||||
//
|
//
|
||||||
// Checksum: b3b80fe96623c1579809528e46d9c75b87bf82ea
|
// Checksum: 3e290e40f4576be99217ddfbd7a76c4d38721af1
|
||||||
//
|
//
|
||||||
// ignore_for_file: unused_import
|
// ignore_for_file: unused_import
|
||||||
|
|
||||||
@ -40,15 +40,30 @@ class ImportCache {
|
|||||||
///
|
///
|
||||||
/// This map's values are the same as the return value of [canonicalize].
|
/// This map's values are the same as the return value of [canonicalize].
|
||||||
///
|
///
|
||||||
/// This cache isn't used for relative imports, because they're
|
/// This cache isn't used for relative imports, because they depend on the
|
||||||
/// context-dependent.
|
/// specific base importer. That's stored separately in
|
||||||
final Map<Tuple2<Uri, bool>, Tuple3<Importer, Uri, Uri>?> _canonicalizeCache;
|
/// [_relativeCanonicalizeCache].
|
||||||
|
final _canonicalizeCache = <Tuple2<Uri, bool>, Tuple3<Importer, Uri, Uri>?>{};
|
||||||
|
|
||||||
|
/// The canonicalized URLs for each non-canonical URL that's resolved using a
|
||||||
|
/// relative importer.
|
||||||
|
///
|
||||||
|
/// The map's keys have four parts:
|
||||||
|
///
|
||||||
|
/// 1. The URL passed to [canonicalize] (the same as in [_canonicalizeCache]).
|
||||||
|
/// 2. Whether the canonicalization is for an `@import` rule.
|
||||||
|
/// 3. The `baseImporter` passed to [canonicalize].
|
||||||
|
/// 4. The `baseUrl` passed to [canonicalize].
|
||||||
|
///
|
||||||
|
/// The map's values are the same as the return value of [canonicalize].
|
||||||
|
final _relativeCanonicalizeCache =
|
||||||
|
<Tuple4<Uri, bool, Importer, Uri?>, Tuple3<Importer, Uri, Uri>?>{};
|
||||||
|
|
||||||
/// The parsed stylesheets for each canonicalized import URL.
|
/// The parsed stylesheets for each canonicalized import URL.
|
||||||
final Map<Uri, Stylesheet?> _importCache;
|
final _importCache = <Uri, Stylesheet?>{};
|
||||||
|
|
||||||
/// The import results for each canonicalized import URL.
|
/// The import results for each canonicalized import URL.
|
||||||
final Map<Uri, ImporterResult> _resultsCache;
|
final _resultsCache = <Uri, ImporterResult>{};
|
||||||
|
|
||||||
/// Creates an import cache that resolves imports using [importers].
|
/// Creates an import cache that resolves imports using [importers].
|
||||||
///
|
///
|
||||||
@ -73,18 +88,12 @@ class ImportCache {
|
|||||||
PackageConfig? packageConfig,
|
PackageConfig? packageConfig,
|
||||||
Logger? logger})
|
Logger? logger})
|
||||||
: _importers = _toImporters(importers, loadPaths, packageConfig),
|
: _importers = _toImporters(importers, loadPaths, packageConfig),
|
||||||
_logger = logger ?? const Logger.stderr(),
|
_logger = logger ?? const Logger.stderr();
|
||||||
_canonicalizeCache = {},
|
|
||||||
_importCache = {},
|
|
||||||
_resultsCache = {};
|
|
||||||
|
|
||||||
/// Creates an import cache without any globally-available importers.
|
/// Creates an import cache without any globally-available importers.
|
||||||
ImportCache.none({Logger? logger})
|
ImportCache.none({Logger? logger})
|
||||||
: _importers = const [],
|
: _importers = const [],
|
||||||
_logger = logger ?? const Logger.stderr(),
|
_logger = logger ?? const Logger.stderr();
|
||||||
_canonicalizeCache = {},
|
|
||||||
_importCache = {},
|
|
||||||
_resultsCache = {};
|
|
||||||
|
|
||||||
/// Converts the user's [importers], [loadPaths], and [packageConfig]
|
/// Converts the user's [importers], [loadPaths], and [packageConfig]
|
||||||
/// options into a single list of importers.
|
/// options into a single list of importers.
|
||||||
@ -117,11 +126,15 @@ class ImportCache {
|
|||||||
Tuple3<Importer, Uri, Uri>? canonicalize(Uri url,
|
Tuple3<Importer, Uri, Uri>? canonicalize(Uri url,
|
||||||
{Importer? baseImporter, Uri? baseUrl, bool forImport = false}) {
|
{Importer? baseImporter, Uri? baseUrl, bool forImport = false}) {
|
||||||
if (baseImporter != null) {
|
if (baseImporter != null) {
|
||||||
var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
|
var relativeResult = _relativeCanonicalizeCache
|
||||||
var canonicalUrl = _canonicalize(baseImporter, resolvedUrl, forImport);
|
.putIfAbsent(Tuple4(url, forImport, baseImporter, baseUrl), () {
|
||||||
if (canonicalUrl != null) {
|
var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
|
||||||
return Tuple3(baseImporter, canonicalUrl, resolvedUrl);
|
var canonicalUrl = _canonicalize(baseImporter, resolvedUrl, forImport);
|
||||||
}
|
if (canonicalUrl != null) {
|
||||||
|
return Tuple3(baseImporter, canonicalUrl, resolvedUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (relativeResult != null) return relativeResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _canonicalizeCache.putIfAbsent(Tuple2(url, forImport), () {
|
return _canonicalizeCache.putIfAbsent(Tuple2(url, forImport), () {
|
||||||
@ -235,6 +248,14 @@ Relative canonical URLs are deprecated and will eventually be disallowed.
|
|||||||
void clearCanonicalize(Uri url) {
|
void clearCanonicalize(Uri url) {
|
||||||
_canonicalizeCache.remove(Tuple2(url, false));
|
_canonicalizeCache.remove(Tuple2(url, false));
|
||||||
_canonicalizeCache.remove(Tuple2(url, true));
|
_canonicalizeCache.remove(Tuple2(url, true));
|
||||||
|
|
||||||
|
var relativeKeysToClear = [
|
||||||
|
for (var key in _relativeCanonicalizeCache.keys)
|
||||||
|
if (key.item1 == url) key
|
||||||
|
];
|
||||||
|
for (var key in relativeKeysToClear) {
|
||||||
|
_relativeCanonicalizeCache.remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the cached parse tree for the stylesheet with the given
|
/// Clears the cached parse tree for the stylesheet with the given
|
||||||
|
@ -183,8 +183,7 @@ T _systemErrorToFileSystemException<T>(T callback()) {
|
|||||||
return callback();
|
return callback();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error is! JsSystemError) rethrow;
|
if (error is! JsSystemError) rethrow;
|
||||||
throw FileSystemException._(
|
throw FileSystemException._(_cleanErrorMessage(error), error.path);
|
||||||
_cleanErrorMessage(error), error.path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,12 @@ abstract class SassNumber extends Value {
|
|||||||
/// integer value, or [assertInt] to do both at once.
|
/// integer value, or [assertInt] to do both at once.
|
||||||
final num value;
|
final num value;
|
||||||
|
|
||||||
|
/// The cached hash code for this number, if it's been computed.
|
||||||
|
///
|
||||||
|
/// @nodoc
|
||||||
|
@protected
|
||||||
|
int? hashCache;
|
||||||
|
|
||||||
/// This number's numerator units.
|
/// This number's numerator units.
|
||||||
List<String> get numeratorUnits;
|
List<String> get numeratorUnits;
|
||||||
|
|
||||||
@ -826,7 +832,7 @@ abstract class SassNumber extends Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int get hashCode => fuzzyHashCode(value *
|
int get hashCode => hashCache ??= fuzzyHashCode(value *
|
||||||
_canonicalMultiplier(numeratorUnits) /
|
_canonicalMultiplier(numeratorUnits) /
|
||||||
_canonicalMultiplier(denominatorUnits));
|
_canonicalMultiplier(denominatorUnits));
|
||||||
|
|
||||||
|
@ -160,5 +160,6 @@ class SingleUnitSassNumber extends SassNumber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int get hashCode => fuzzyHashCode(value * canonicalMultiplierForUnit(_unit));
|
int get hashCode =>
|
||||||
|
hashCache ??= fuzzyHashCode(value * canonicalMultiplierForUnit(_unit));
|
||||||
}
|
}
|
||||||
|
@ -142,5 +142,5 @@ class UnitlessSassNumber extends SassNumber {
|
|||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
other is UnitlessSassNumber && fuzzyEquals(value, other.value);
|
other is UnitlessSassNumber && fuzzyEquals(value, other.value);
|
||||||
|
|
||||||
int get hashCode => fuzzyHashCode(value);
|
int get hashCode => hashCache ??= fuzzyHashCode(value);
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,9 @@ class SassString extends Value {
|
|||||||
/// efficient.
|
/// efficient.
|
||||||
late final int sassLength = text.runes.length;
|
late final int sassLength = text.runes.length;
|
||||||
|
|
||||||
|
/// The cached hash code for this number, if it's been computed.
|
||||||
|
int? _hashCache;
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@internal
|
@internal
|
||||||
bool get isSpecialNumber {
|
bool get isSpecialNumber {
|
||||||
@ -189,7 +192,7 @@ class SassString extends Value {
|
|||||||
|
|
||||||
bool operator ==(Object other) => other is SassString && text == other.text;
|
bool operator ==(Object other) => other is SassString && text == other.text;
|
||||||
|
|
||||||
int get hashCode => text.hashCode;
|
int get hashCode => _hashCache ??= text.hashCode;
|
||||||
|
|
||||||
/// Throws a [SassScriptException] with the given [message].
|
/// Throws a [SassScriptException] with the given [message].
|
||||||
SassScriptException _exception(String message, [String? name]) =>
|
SassScriptException _exception(String message, [String? name]) =>
|
||||||
|
@ -23,6 +23,7 @@ import '../callable.dart';
|
|||||||
import '../color_names.dart';
|
import '../color_names.dart';
|
||||||
import '../configuration.dart';
|
import '../configuration.dart';
|
||||||
import '../configured_value.dart';
|
import '../configured_value.dart';
|
||||||
|
import '../evaluation_context.dart';
|
||||||
import '../exception.dart';
|
import '../exception.dart';
|
||||||
import '../extend/extension_store.dart';
|
import '../extend/extension_store.dart';
|
||||||
import '../extend/extension.dart';
|
import '../extend/extension.dart';
|
||||||
@ -39,7 +40,6 @@ import '../syntax.dart';
|
|||||||
import '../utils.dart';
|
import '../utils.dart';
|
||||||
import '../util/nullable.dart';
|
import '../util/nullable.dart';
|
||||||
import '../value.dart';
|
import '../value.dart';
|
||||||
import '../warn.dart';
|
|
||||||
import 'interface/css.dart';
|
import 'interface/css.dart';
|
||||||
import 'interface/expression.dart';
|
import 'interface/expression.dart';
|
||||||
import 'interface/modifiable_css.dart';
|
import 'interface/modifiable_css.dart';
|
||||||
@ -498,7 +498,7 @@ class _EvaluateVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<EvaluateResult> run(AsyncImporter? importer, Stylesheet node) async {
|
Future<EvaluateResult> run(AsyncImporter? importer, Stylesheet node) async {
|
||||||
return _withWarnCallback(node, () async {
|
return withEvaluationContext(_EvaluationContext(this, node), () async {
|
||||||
var url = node.span.sourceUrl;
|
var url = node.span.sourceUrl;
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
_activeModules[url] = null;
|
_activeModules[url] = null;
|
||||||
@ -512,29 +512,17 @@ class _EvaluateVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Value> runExpression(AsyncImporter? importer, Expression expression) =>
|
Future<Value> runExpression(AsyncImporter? importer, Expression expression) =>
|
||||||
_withWarnCallback(
|
withEvaluationContext(
|
||||||
expression,
|
_EvaluationContext(this, expression),
|
||||||
() => _withFakeStylesheet(
|
() => _withFakeStylesheet(
|
||||||
importer, expression, () => expression.accept(this)));
|
importer, expression, () => expression.accept(this)));
|
||||||
|
|
||||||
Future<void> runStatement(AsyncImporter? importer, Statement statement) =>
|
Future<void> runStatement(AsyncImporter? importer, Statement statement) =>
|
||||||
_withWarnCallback(
|
withEvaluationContext(
|
||||||
statement,
|
_EvaluationContext(this, statement),
|
||||||
() => _withFakeStylesheet(
|
() => _withFakeStylesheet(
|
||||||
importer, statement, () => statement.accept(this)));
|
importer, statement, () => statement.accept(this)));
|
||||||
|
|
||||||
/// Runs [callback] with a definition for the top-level `warn` function.
|
|
||||||
///
|
|
||||||
/// If no other span can be found to report a warning, falls back on
|
|
||||||
/// [nodeWithSpan]'s.
|
|
||||||
T _withWarnCallback<T>(AstNode nodeWithSpan, T callback()) {
|
|
||||||
return withWarnCallback(
|
|
||||||
(message, deprecation) => _warn(
|
|
||||||
message, _importSpan ?? _callableNode?.span ?? nodeWithSpan.span,
|
|
||||||
deprecation: deprecation),
|
|
||||||
callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asserts that [value] is not `null` and returns it.
|
/// Asserts that [value] is not `null` and returns it.
|
||||||
///
|
///
|
||||||
/// This is used for fields that are set whenever the evaluator is evaluating
|
/// This is used for fields that are set whenever the evaluator is evaluating
|
||||||
@ -2590,8 +2578,7 @@ class _EvaluateVisitor
|
|||||||
|
|
||||||
Value result;
|
Value result;
|
||||||
try {
|
try {
|
||||||
result = await withCurrentCallableNode(
|
result = await callback(evaluated.positional);
|
||||||
nodeWithSpan, () => callback(evaluated.positional));
|
|
||||||
} on SassRuntimeException {
|
} on SassRuntimeException {
|
||||||
rethrow;
|
rethrow;
|
||||||
} on MultiSpanSassScriptException catch (error) {
|
} on MultiSpanSassScriptException catch (error) {
|
||||||
@ -3462,6 +3449,34 @@ class EvaluateResult {
|
|||||||
EvaluateResult(this.stylesheet, this.loadedUrls);
|
EvaluateResult(this.stylesheet, this.loadedUrls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation of [EvaluationContext] using the information available in
|
||||||
|
/// [_EvaluateVisitor].
|
||||||
|
class _EvaluationContext implements EvaluationContext {
|
||||||
|
/// The visitor backing this context.
|
||||||
|
final _EvaluateVisitor _visitor;
|
||||||
|
|
||||||
|
/// The AST node whose span should be used for [warn] if no other span is
|
||||||
|
/// avaiable.
|
||||||
|
final AstNode _defaultWarnNodeWithSpan;
|
||||||
|
|
||||||
|
_EvaluationContext(this._visitor, this._defaultWarnNodeWithSpan);
|
||||||
|
|
||||||
|
FileSpan get currentCallableSpan {
|
||||||
|
var callableNode = _visitor._callableNode;
|
||||||
|
if (callableNode != null) return callableNode.span;
|
||||||
|
throw StateError("No Sass callable is currently being evaluated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(String message, {bool deprecation = false}) {
|
||||||
|
_visitor._warn(
|
||||||
|
message,
|
||||||
|
_visitor._importSpan ??
|
||||||
|
_visitor._callableNode?.span ??
|
||||||
|
_defaultWarnNodeWithSpan.span,
|
||||||
|
deprecation: deprecation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of evaluating arguments to a function or mixin.
|
/// The result of evaluating arguments to a function or mixin.
|
||||||
class _ArgumentResults {
|
class _ArgumentResults {
|
||||||
/// Arguments passed by position.
|
/// Arguments passed by position.
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
||||||
// See tool/grind/synchronize.dart for details.
|
// See tool/grind/synchronize.dart for details.
|
||||||
//
|
//
|
||||||
// Checksum: 7a3ae06379ca09dbf3e92d01c1fd974c5b3b9154
|
// Checksum: 5cdb3467b517bf381d525a1a4bc4f9b6a0eeefad
|
||||||
//
|
//
|
||||||
// ignore_for_file: unused_import
|
// ignore_for_file: unused_import
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ import '../callable.dart';
|
|||||||
import '../color_names.dart';
|
import '../color_names.dart';
|
||||||
import '../configuration.dart';
|
import '../configuration.dart';
|
||||||
import '../configured_value.dart';
|
import '../configured_value.dart';
|
||||||
|
import '../evaluation_context.dart';
|
||||||
import '../exception.dart';
|
import '../exception.dart';
|
||||||
import '../extend/extension_store.dart';
|
import '../extend/extension_store.dart';
|
||||||
import '../extend/extension.dart';
|
import '../extend/extension.dart';
|
||||||
@ -48,7 +49,6 @@ import '../syntax.dart';
|
|||||||
import '../utils.dart';
|
import '../utils.dart';
|
||||||
import '../util/nullable.dart';
|
import '../util/nullable.dart';
|
||||||
import '../value.dart';
|
import '../value.dart';
|
||||||
import '../warn.dart';
|
|
||||||
import 'interface/css.dart';
|
import 'interface/css.dart';
|
||||||
import 'interface/expression.dart';
|
import 'interface/expression.dart';
|
||||||
import 'interface/modifiable_css.dart';
|
import 'interface/modifiable_css.dart';
|
||||||
@ -503,7 +503,7 @@ class _EvaluateVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
EvaluateResult run(Importer? importer, Stylesheet node) {
|
EvaluateResult run(Importer? importer, Stylesheet node) {
|
||||||
return _withWarnCallback(node, () {
|
return withEvaluationContext(_EvaluationContext(this, node), () {
|
||||||
var url = node.span.sourceUrl;
|
var url = node.span.sourceUrl;
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
_activeModules[url] = null;
|
_activeModules[url] = null;
|
||||||
@ -517,29 +517,17 @@ class _EvaluateVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Value runExpression(Importer? importer, Expression expression) =>
|
Value runExpression(Importer? importer, Expression expression) =>
|
||||||
_withWarnCallback(
|
withEvaluationContext(
|
||||||
expression,
|
_EvaluationContext(this, expression),
|
||||||
() => _withFakeStylesheet(
|
() => _withFakeStylesheet(
|
||||||
importer, expression, () => expression.accept(this)));
|
importer, expression, () => expression.accept(this)));
|
||||||
|
|
||||||
void runStatement(Importer? importer, Statement statement) =>
|
void runStatement(Importer? importer, Statement statement) =>
|
||||||
_withWarnCallback(
|
withEvaluationContext(
|
||||||
statement,
|
_EvaluationContext(this, statement),
|
||||||
() => _withFakeStylesheet(
|
() => _withFakeStylesheet(
|
||||||
importer, statement, () => statement.accept(this)));
|
importer, statement, () => statement.accept(this)));
|
||||||
|
|
||||||
/// Runs [callback] with a definition for the top-level `warn` function.
|
|
||||||
///
|
|
||||||
/// If no other span can be found to report a warning, falls back on
|
|
||||||
/// [nodeWithSpan]'s.
|
|
||||||
T _withWarnCallback<T>(AstNode nodeWithSpan, T callback()) {
|
|
||||||
return withWarnCallback(
|
|
||||||
(message, deprecation) => _warn(
|
|
||||||
message, _importSpan ?? _callableNode?.span ?? nodeWithSpan.span,
|
|
||||||
deprecation: deprecation),
|
|
||||||
callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Asserts that [value] is not `null` and returns it.
|
/// Asserts that [value] is not `null` and returns it.
|
||||||
///
|
///
|
||||||
/// This is used for fields that are set whenever the evaluator is evaluating
|
/// This is used for fields that are set whenever the evaluator is evaluating
|
||||||
@ -2573,8 +2561,7 @@ class _EvaluateVisitor
|
|||||||
|
|
||||||
Value result;
|
Value result;
|
||||||
try {
|
try {
|
||||||
result = withCurrentCallableNode(
|
result = callback(evaluated.positional);
|
||||||
nodeWithSpan, () => callback(evaluated.positional));
|
|
||||||
} on SassRuntimeException {
|
} on SassRuntimeException {
|
||||||
rethrow;
|
rethrow;
|
||||||
} on MultiSpanSassScriptException catch (error) {
|
} on MultiSpanSassScriptException catch (error) {
|
||||||
@ -3403,6 +3390,34 @@ class _ImportedCssVisitor implements ModifiableCssVisitor<void> {
|
|||||||
_visitor._addChild(node, through: (node) => node is CssStyleRule);
|
_visitor._addChild(node, through: (node) => node is CssStyleRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation of [EvaluationContext] using the information available in
|
||||||
|
/// [_EvaluateVisitor].
|
||||||
|
class _EvaluationContext implements EvaluationContext {
|
||||||
|
/// The visitor backing this context.
|
||||||
|
final _EvaluateVisitor _visitor;
|
||||||
|
|
||||||
|
/// The AST node whose span should be used for [warn] if no other span is
|
||||||
|
/// avaiable.
|
||||||
|
final AstNode _defaultWarnNodeWithSpan;
|
||||||
|
|
||||||
|
_EvaluationContext(this._visitor, this._defaultWarnNodeWithSpan);
|
||||||
|
|
||||||
|
FileSpan get currentCallableSpan {
|
||||||
|
var callableNode = _visitor._callableNode;
|
||||||
|
if (callableNode != null) return callableNode.span;
|
||||||
|
throw StateError("No Sass callable is currently being evaluated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(String message, {bool deprecation = false}) {
|
||||||
|
_visitor._warn(
|
||||||
|
message,
|
||||||
|
_visitor._importSpan ??
|
||||||
|
_visitor._callableNode?.span ??
|
||||||
|
_defaultWarnNodeWithSpan.span,
|
||||||
|
deprecation: deprecation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of evaluating arguments to a function or mixin.
|
/// The result of evaluating arguments to a function or mixin.
|
||||||
class _ArgumentResults {
|
class _ArgumentResults {
|
||||||
/// Arguments passed by position.
|
/// Arguments passed by position.
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright 2019 Google Inc. Use of this source code is governed by an
|
|
||||||
// MIT-style license that can be found in the LICENSE file or at
|
|
||||||
// https://opensource.org/licenses/MIT.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
/// Prints a warning message associated with the current `@import` or function
|
|
||||||
/// call.
|
|
||||||
///
|
|
||||||
/// If [deprecation] is `true`, the warning is emitted as a deprecation warning.
|
|
||||||
///
|
|
||||||
/// This may only be called within a custom function or importer callback.
|
|
||||||
///
|
|
||||||
/// {@category Compile}
|
|
||||||
void warn(String message, {bool deprecation = false}) {
|
|
||||||
var warnDefinition = Zone.current[#_warn];
|
|
||||||
|
|
||||||
if (warnDefinition == null) {
|
|
||||||
throw ArgumentError(
|
|
||||||
"warn() may only be called within a custom function or importer "
|
|
||||||
"callback.");
|
|
||||||
}
|
|
||||||
|
|
||||||
warnDefinition(message, deprecation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs [callback] with [warn] as the definition for the top-level `warn()` function.
|
|
||||||
///
|
|
||||||
/// This is zone-based, so if [callback] is asynchronous [warn] is set for the
|
|
||||||
/// duration of that callback.
|
|
||||||
T withWarnCallback<T>(
|
|
||||||
void warn(String message, bool deprecation), T callback()) {
|
|
||||||
return runZoned(() {
|
|
||||||
return callback();
|
|
||||||
}, zoneValues: {#_warn: warn});
|
|
||||||
}
|
|
130
perf.md
130
perf.md
@ -3,8 +3,8 @@ the benefit Dart Sass could provide relative to other implementations.
|
|||||||
|
|
||||||
This was tested against:
|
This was tested against:
|
||||||
|
|
||||||
* libsass d4d74ef5 and sassc 66f0ef3 compiled with g++ (Debian 10.2.0-16) 10.2.0.
|
* libsass da91d985 and sassc 66f0ef3 compiled with g++ (Debian 10.3.0-11) 10.3.0.
|
||||||
* Dart Sass ae967c7 on Dart 2.10.4 (stable) (Wed Nov 11 13:35:58 2020 +0100) on "linux_x64" and Node v14.7.0.
|
* Dart Sass bf318a8 on Dart 2.14.1 (stable) (Wed Sep 8 13:33:08 2021 +0200) on "linux_x64" and Node v16.10.0.
|
||||||
|
|
||||||
on Debian x64 with Intel Core i7-8650U CPU @ 1.90GHz.
|
on Debian x64 with Intel Core i7-8650U CPU @ 1.90GHz.
|
||||||
|
|
||||||
@ -17,138 +17,138 @@ I ran five instances of each configuration and recorded the fastest time.
|
|||||||
Running on a file containing 4 instances of `.foo {a: b}`:
|
Running on a file containing 4 instances of `.foo {a: b}`:
|
||||||
|
|
||||||
* sassc: 0.002s
|
* sassc: 0.002s
|
||||||
* Dart Sass from a script snapshot: 0.179s
|
* Dart Sass from a script snapshot: 0.177s
|
||||||
* Dart Sass native executable: 0.009s
|
* Dart Sass native executable: 0.009s
|
||||||
* Dart Sass on Node.js: 0.248s
|
* Dart Sass on Node.js: 0.219s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 4.5x slower than libsass
|
* 4.5x slower than libsass
|
||||||
* 27.6x faster than Dart Sass on Node
|
* 24.3x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Large Plain CSS
|
## Large Plain CSS
|
||||||
|
|
||||||
Running on a file containing 2^17 instances of `.foo {a: b}`:
|
Running on a file containing 2^17 instances of `.foo {a: b}`:
|
||||||
|
|
||||||
* sassc: 1.770s
|
* sassc: 1.607s
|
||||||
* Dart Sass from a script snapshot: 1.548s
|
* Dart Sass from a script snapshot: 1.643s
|
||||||
* Dart Sass native executable: 1.379s
|
* Dart Sass native executable: 1.473s
|
||||||
* Dart Sass on Node.js: 2.587s
|
* Dart Sass on Node.js: 2.529s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.3x faster than libsass
|
* 1.1x faster than libsass
|
||||||
* 1.9x faster than Dart Sass on Node
|
* 1.7x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Preceding Sparse `@extend`
|
## Preceding Sparse `@extend`
|
||||||
|
|
||||||
Running on a file containing `.x {@extend .y}`, 2^17 instances of `.foo {a: b}`, and then `.y {a: b}`:
|
Running on a file containing `.x {@extend .y}`, 2^17 instances of `.foo {a: b}`, and then `.y {a: b}`:
|
||||||
|
|
||||||
* sassc: 1.797s
|
* sassc: 1.643s
|
||||||
* Dart Sass from a script snapshot: 1.594s
|
* Dart Sass from a script snapshot: 1.723s
|
||||||
* Dart Sass native executable: 1.490s
|
* Dart Sass native executable: 1.535s
|
||||||
* Dart Sass on Node.js: 2.783s
|
* Dart Sass on Node.js: 2.574s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.2x faster than libsass
|
* 1.1x faster than libsass
|
||||||
* 1.9x faster than Dart Sass on Node
|
* 1.7x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Following Sparse `@extend`
|
## Following Sparse `@extend`
|
||||||
|
|
||||||
Running on a file containing `.y {a: b}`, 2^17 instances of `.foo {a: b}`, and then `.x {@extend .y}`:
|
Running on a file containing `.y {a: b}`, 2^17 instances of `.foo {a: b}`, and then `.x {@extend .y}`:
|
||||||
|
|
||||||
* sassc: 1.902s
|
* sassc: 1.642s
|
||||||
* Dart Sass from a script snapshot: 1.587s
|
* Dart Sass from a script snapshot: 1.676s
|
||||||
* Dart Sass native executable: 1.425s
|
* Dart Sass native executable: 1.517s
|
||||||
* Dart Sass on Node.js: 2.550s
|
* Dart Sass on Node.js: 2.547s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.3x faster than libsass
|
* 1.1x faster than libsass
|
||||||
* 1.8x faster than Dart Sass on Node
|
* 1.7x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Preceding Dense `@extend`
|
## Preceding Dense `@extend`
|
||||||
|
|
||||||
Running on a file containing `.bar {@extend .foo}` followed by 2^17 instances of `.foo {a: b}`:
|
Running on a file containing `.bar {@extend .foo}` followed by 2^17 instances of `.foo {a: b}`:
|
||||||
|
|
||||||
* sassc: 2.556s
|
* sassc: 2.336s
|
||||||
* Dart Sass from a script snapshot: 2.426s
|
* Dart Sass from a script snapshot: 2.453s
|
||||||
* Dart Sass native executable: 2.293s
|
* Dart Sass native executable: 2.312s
|
||||||
* Dart Sass on Node.js: 4.843s
|
* Dart Sass on Node.js: 5.874s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.1x faster than libsass
|
* identical to libsass
|
||||||
* 2.1x faster than Dart Sass on Node
|
* 2.5x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Following Dense `@extend`
|
## Following Dense `@extend`
|
||||||
|
|
||||||
Running on a file containing 2^17 instances of `.foo {a: b}` followed by `.bar {@extend .foo}`:
|
Running on a file containing 2^17 instances of `.foo {a: b}` followed by `.bar {@extend .foo}`:
|
||||||
|
|
||||||
* sassc: 2.567s
|
* sassc: 2.353s
|
||||||
* Dart Sass from a script snapshot: 2.270s
|
* Dart Sass from a script snapshot: 2.357s
|
||||||
* Dart Sass native executable: 2.174s
|
* Dart Sass native executable: 2.220s
|
||||||
* Dart Sass on Node.js: 4.285s
|
* Dart Sass on Node.js: 5.587s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.2x faster than libsass
|
* 1.1x faster than libsass
|
||||||
* 2.0x faster than Dart Sass on Node
|
* 2.5x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Bootstrap
|
## Bootstrap
|
||||||
|
|
||||||
Running on a file containing 16 instances of importing the Bootstrap framework:
|
Running on a file containing 16 instances of importing the Bootstrap framework:
|
||||||
|
|
||||||
* sassc: 0.798s
|
* sassc: 0.789s
|
||||||
* Dart Sass from a script snapshot: 1.417s
|
* Dart Sass from a script snapshot: 1.517s
|
||||||
* Dart Sass native executable: 0.708s
|
* Dart Sass native executable: 0.691s
|
||||||
* Dart Sass on Node.js: 2.832s
|
* Dart Sass on Node.js: 2.799s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.1x faster than libsass
|
* 1.1x faster than libsass
|
||||||
* 4.0x faster than Dart Sass on Node
|
* 4.1x faster than Dart Sass on Node
|
||||||
|
|
||||||
## a11ycolor
|
## a11ycolor
|
||||||
|
|
||||||
Running on a file containing test cases for a computation-intensive color-processing library:
|
Running on a file containing test cases for a computation-intensive color-processing library:
|
||||||
|
|
||||||
* sassc: 0.239s
|
* sassc: 0.205s
|
||||||
* Dart Sass from a script snapshot: 0.661s
|
* Dart Sass from a script snapshot: 0.649s
|
||||||
* Dart Sass native executable: 0.319s
|
* Dart Sass native executable: 0.245s
|
||||||
* Dart Sass on Node.js: 0.882s
|
* Dart Sass on Node.js: 0.827s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 1.3x slower than libsass
|
* 1.2x slower than libsass
|
||||||
* 2.8x faster than Dart Sass on Node
|
* 3.4x faster than Dart Sass on Node
|
||||||
|
|
||||||
## Susy
|
|
||||||
|
|
||||||
Running on a file containing test cases for the computation-intensive Susy grid framework:
|
|
||||||
|
|
||||||
* sassc: 0.201s
|
|
||||||
* Dart Sass from a script snapshot: 0.706s
|
|
||||||
* Dart Sass native executable: 0.141s
|
|
||||||
* Dart Sass on Node.js: 1.187s
|
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
|
||||||
|
|
||||||
* 1.4x faster than libsass
|
|
||||||
* 8.4x faster than Dart Sass on Node
|
|
||||||
|
|
||||||
## Duomo
|
## Duomo
|
||||||
|
|
||||||
Running on a file containing the output of the numerically-intensive Duomo framework:
|
Running on a file containing the output of the numerically-intensive Duomo framework (skipping LibSass due to module system use):
|
||||||
|
|
||||||
* Dart Sass from a script snapshot: 2.017s
|
* Dart Sass from a script snapshot: 2.150s
|
||||||
* Dart Sass native executable: 1.213s
|
* Dart Sass native executable: 1.406s
|
||||||
* Dart Sass on Node.js: 3.632s
|
* Dart Sass on Node.js: 4.449s
|
||||||
|
|
||||||
Based on these numbers, Dart Sass from a native executable is approximately:
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
* 3.0x faster than Dart Sass on Node
|
* 3.2x faster than Dart Sass on Node
|
||||||
|
|
||||||
|
## Carbon
|
||||||
|
|
||||||
|
Running on a file containing the output of the import-intensive Carbon framework:
|
||||||
|
|
||||||
|
* sassc: 7.481s
|
||||||
|
* Dart Sass from a script snapshot: 5.891s
|
||||||
|
* Dart Sass native executable: 5.734s
|
||||||
|
* Dart Sass on Node.js: 15.725s
|
||||||
|
|
||||||
|
Based on these numbers, Dart Sass from a native executable is approximately:
|
||||||
|
|
||||||
|
* 1.3x faster than libsass
|
||||||
|
* 2.7x faster than Dart Sass on Node
|
||||||
|
|
||||||
# Prior Measurements
|
# Prior Measurements
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
## 1.0.0-beta.17
|
||||||
|
|
||||||
|
* No user-visible changes.
|
||||||
|
|
||||||
## 1.0.0-beta.16
|
## 1.0.0-beta.16
|
||||||
|
|
||||||
* No user-visible changes.
|
* No user-visible changes.
|
||||||
|
@ -2,7 +2,7 @@ name: sass_api
|
|||||||
# Note: Every time we add a new Sass AST node, we need to bump the *major*
|
# Note: Every time we add a new Sass AST node, we need to bump the *major*
|
||||||
# version because it's a breaking change for anyone who's implementing the
|
# version because it's a breaking change for anyone who's implementing the
|
||||||
# visitor interface(s).
|
# visitor interface(s).
|
||||||
version: 1.0.0-beta.16
|
version: 1.0.0-beta.17
|
||||||
description: Additional APIs for Dart Sass.
|
description: Additional APIs for Dart Sass.
|
||||||
homepage: https://github.com/sass/dart-sass
|
homepage: https://github.com/sass/dart-sass
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ environment:
|
|||||||
sdk: '>=2.12.0 <3.0.0'
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
sass: 1.43.2
|
sass: 1.43.3
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
sass: {path: ../..}
|
sass: {path: ../..}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: sass
|
name: sass
|
||||||
version: 1.43.2
|
version: 1.43.3
|
||||||
description: A Sass implementation in Dart.
|
description: A Sass implementation in Dart.
|
||||||
homepage: https://github.com/sass/dart-sass
|
homepage: https://github.com/sass/dart-sass
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("throws an error outside a callback", () {
|
test("throws an error outside a callback", () {
|
||||||
expect(() => warn("heck"), throwsArgumentError);
|
expect(() => warn("heck"), throwsStateError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -55,14 +55,20 @@ Future<void> benchmarkGenerate() async {
|
|||||||
}
|
}
|
||||||
""");
|
""");
|
||||||
|
|
||||||
var susy = await cloneOrCheckout("https://github.com/oddbird/susy", "v3.0.5");
|
|
||||||
await runAsync("npm", arguments: ["install"], workingDirectory: susy);
|
|
||||||
File("${sources.path}/susy.scss")
|
|
||||||
.writeAsStringSync("@import '../susy/test/scss/test.scss'");
|
|
||||||
|
|
||||||
await cloneOrCheckout("https://github.com/zaydek/duomo", "v0.7.12");
|
await cloneOrCheckout("https://github.com/zaydek/duomo", "v0.7.12");
|
||||||
File("${sources.path}/duomo.scss")
|
File("${sources.path}/duomo.scss")
|
||||||
.writeAsStringSync("@import '../duomo/scripts/duomo.scss'");
|
.writeAsStringSync("@import '../duomo/scripts/duomo.scss'");
|
||||||
|
|
||||||
|
// Note: This version only supports Node Sass 5.x, which only supports up to
|
||||||
|
// Node 14.x. Once there's a version that support Node Sass 6.x, we should use
|
||||||
|
// that instead.
|
||||||
|
var carbon = await cloneOrCheckout(
|
||||||
|
"https://github.com/carbon-design-system/ibm-cloud-cognitive",
|
||||||
|
"@carbon/ibm-cloud-cognitive@0.93.2");
|
||||||
|
await runAsync("npm", arguments: ["install"], workingDirectory: carbon);
|
||||||
|
File("${sources.path}/carbon.scss")
|
||||||
|
.writeAsStringSync("@import '../ibm-cloud-cognitive/packages/"
|
||||||
|
"cloud-cognitive/src/index-without-carbon-released-only'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes [times] instances of [text] to [path].
|
/// Writes [times] instances of [text] to [path].
|
||||||
@ -96,9 +102,8 @@ Future<void> _writeNTimes(String path, String text, num times,
|
|||||||
"pkg-npm-release")
|
"pkg-npm-release")
|
||||||
Future<void> benchmark() async {
|
Future<void> benchmark() async {
|
||||||
var libsass =
|
var libsass =
|
||||||
await cloneOrCheckout('https://github.com/sass/libsass', 'origin/master');
|
await cloneOrCheckout('https://github.com/sass/libsass', 'master');
|
||||||
var sassc =
|
var sassc = await cloneOrCheckout('https://github.com/sass/sassc', 'master');
|
||||||
await cloneOrCheckout('https://github.com/sass/sassc', 'origin/master');
|
|
||||||
|
|
||||||
await runAsync("make",
|
await runAsync("make",
|
||||||
runOptions: RunOptions(
|
runOptions: RunOptions(
|
||||||
@ -161,15 +166,18 @@ I ran five instances of each configuration and recorded the fastest time.
|
|||||||
"a11ycolor",
|
"a11ycolor",
|
||||||
"test cases for a computation-intensive color-processing library"
|
"test cases for a computation-intensive color-processing library"
|
||||||
],
|
],
|
||||||
[
|
|
||||||
"susy.scss",
|
|
||||||
"Susy",
|
|
||||||
"test cases for the computation-intensive Susy grid framework"
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
"duomo.scss",
|
"duomo.scss",
|
||||||
"Duomo",
|
"Duomo",
|
||||||
"the output of the numerically-intensive Duomo framework"
|
"the output of the numerically-intensive Duomo framework "
|
||||||
|
"(skipping LibSass due to module system use)"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"carbon.scss",
|
||||||
|
"Carbon",
|
||||||
|
"the output of the import-intensive Carbon framework",
|
||||||
|
"-I",
|
||||||
|
"build/ibm-cloud-cognitive/node_modules"
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -179,6 +187,7 @@ I ran five instances of each configuration and recorded the fastest time.
|
|||||||
var path = p.join('build/benchmark', info[0]);
|
var path = p.join('build/benchmark', info[0]);
|
||||||
var title = info[1];
|
var title = info[1];
|
||||||
var description = info[2];
|
var description = info[2];
|
||||||
|
var extraArgs = info.sublist(3);
|
||||||
|
|
||||||
buffer.writeln("## $title");
|
buffer.writeln("## $title");
|
||||||
buffer.writeln();
|
buffer.writeln();
|
||||||
@ -187,23 +196,27 @@ I ran five instances of each configuration and recorded the fastest time.
|
|||||||
|
|
||||||
Duration? sasscTime;
|
Duration? sasscTime;
|
||||||
if (!libsassIncompatible.contains(info[1])) {
|
if (!libsassIncompatible.contains(info[1])) {
|
||||||
sasscTime = await _benchmark(p.join(sassc, 'bin', 'sassc'), [path]);
|
sasscTime =
|
||||||
|
await _benchmark(p.join(sassc, 'bin', 'sassc'), [path, ...extraArgs]);
|
||||||
buffer.writeln("* sassc: ${_formatTime(sasscTime)}");
|
buffer.writeln("* sassc: ${_formatTime(sasscTime)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var scriptSnapshotTime = await _benchmark(Platform.executable,
|
var scriptSnapshotTime = await _benchmark(Platform.executable, [
|
||||||
['--no-enable-asserts', p.join('build', 'sass.snapshot'), path]);
|
'--no-enable-asserts',
|
||||||
|
p.join('build', 'sass.snapshot'),
|
||||||
|
path,
|
||||||
|
...extraArgs
|
||||||
|
]);
|
||||||
buffer.writeln("* Dart Sass from a script snapshot: "
|
buffer.writeln("* Dart Sass from a script snapshot: "
|
||||||
"${_formatTime(scriptSnapshotTime)}");
|
"${_formatTime(scriptSnapshotTime)}");
|
||||||
|
|
||||||
var nativeExecutableTime = await _benchmark(
|
var nativeExecutableTime =
|
||||||
p.join(sdkDir.path, 'bin/dartaotruntime'),
|
await _benchmark(p.join('build', 'sass.native'), [path, ...extraArgs]);
|
||||||
[p.join('build', 'sass.native'), path]);
|
|
||||||
buffer.writeln("* Dart Sass native executable: "
|
buffer.writeln("* Dart Sass native executable: "
|
||||||
"${_formatTime(nativeExecutableTime)}");
|
"${_formatTime(nativeExecutableTime)}");
|
||||||
|
|
||||||
var nodeTime =
|
var nodeTime = await _benchmark(
|
||||||
await _benchmark("node", [p.join('build', 'npm', 'sass.js'), path]);
|
"node", [p.join('build', 'npm', 'sass.js'), path, ...extraArgs]);
|
||||||
buffer.writeln("* Dart Sass on Node.js: ${_formatTime(nodeTime)}");
|
buffer.writeln("* Dart Sass on Node.js: ${_formatTime(nodeTime)}");
|
||||||
|
|
||||||
buffer.writeln();
|
buffer.writeln();
|
||||||
|
Loading…
Reference in New Issue
Block a user