diff --git a/CHANGELOG.md b/CHANGELOG.md index 523a1a7cd..c7e3a4a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.56.2 + +### Embedded Sass + +* The embedded compiler now supports version 1.2.0 of [the embedded + protocol](https://github.com/sass/embedded-protocol). + ## 1.56.1 ### Embedded Sass diff --git a/lib/src/callable.dart b/lib/src/callable.dart index 7f8b0c383..28f65c2b1 100644 --- a/lib/src/callable.dart +++ b/lib/src/callable.dart @@ -3,10 +3,13 @@ // https://opensource.org/licenses/MIT. import 'package:meta/meta.dart'; +import 'package:tuple/tuple.dart'; +import 'ast/sass.dart'; import 'callable/async.dart'; import 'callable/built_in.dart'; import 'exception.dart'; +import 'utils.dart'; import 'value.dart'; export 'callable/async.dart'; @@ -117,4 +120,15 @@ abstract class Callable extends AsyncCallable { factory Callable.function(String name, String arguments, Value callback(List arguments)) => BuiltInCallable.function(name, arguments, callback); + + /// Creates a callable with a single [signature] and a single [callback]. + /// + /// Throws a [SassFormatException] if parsing fails. + factory Callable.fromSignature( + String signature, Value callback(List arguments), + {bool requireParens = true}) { + Tuple2 tuple = + parseSignature(signature, requireParens: requireParens); + return BuiltInCallable.parsed(tuple.item1, tuple.item2, callback); + } } diff --git a/lib/src/callable/async.dart b/lib/src/callable/async.dart index 28965bc80..7a77d243a 100644 --- a/lib/src/callable/async.dart +++ b/lib/src/callable/async.dart @@ -5,8 +5,11 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:tuple/tuple.dart'; +import '../ast/sass.dart'; import '../exception.dart'; +import '../utils.dart'; import '../value.dart'; import 'async_built_in.dart'; @@ -40,4 +43,15 @@ abstract class AsyncCallable { factory AsyncCallable.function(String name, String arguments, FutureOr callback(List arguments)) => AsyncBuiltInCallable.function(name, arguments, callback); + + /// Creates a callable with a single [signature] and a single [callback]. + /// + /// Throws a [SassFormatException] if parsing fails. + factory AsyncCallable.fromSignature( + String signature, FutureOr callback(List arguments), + {bool requireParens = true}) { + Tuple2 tuple = + parseSignature(signature, requireParens: requireParens); + return AsyncBuiltInCallable.parsed(tuple.item1, tuple.item2, callback); + } } diff --git a/lib/src/node/compile.dart b/lib/src/node/compile.dart index ebbd55add..a5a5e50f2 100644 --- a/lib/src/node/compile.dart +++ b/lib/src/node/compile.dart @@ -6,12 +6,8 @@ import 'package:js/js.dart'; import 'package:node_interop/js.dart'; import 'package:node_interop/util.dart' hide futureToPromise; import 'package:term_glyph/term_glyph.dart' as glyph; -import 'package:tuple/tuple.dart'; import '../../sass.dart'; -import '../ast/sass.dart'; -import '../callable.dart'; -import '../exception.dart'; import '../importer/no_op.dart'; import '../importer/node_to_dart/async.dart'; import '../importer/node_to_dart/async_file.dart'; @@ -19,9 +15,7 @@ import '../importer/node_to_dart/file.dart'; import '../importer/node_to_dart/sync.dart'; import '../io.dart'; import '../logger/node_to_dart.dart'; -import '../parse/scss.dart'; import '../util/nullable.dart'; -import '../utils.dart'; import 'compile_options.dart'; import 'compile_result.dart'; import 'exception.dart'; @@ -236,33 +230,25 @@ List _parseFunctions(Object? functions, {bool asynch = false}) { var result = []; jsForEach(functions, (signature, callback) { - Tuple2 tuple; - try { - tuple = ScssParser(signature).parseSignature(); - } on SassFormatException catch (error, stackTrace) { - throwWithTrace( - SassFormatException( - 'Invalid signature "$signature": ${error.message}', error.span), - stackTrace); - } - if (!asynch) { - result.add(BuiltInCallable.parsed(tuple.item1, tuple.item2, (arguments) { + late Callable callable; + callable = Callable.fromSignature(signature, (arguments) { var result = (callback as Function)(toJSArray(arguments)); if (result is Value) return result; if (isPromise(result)) { throw 'Invalid return value for custom function ' - '"${tuple.item1}":\n' + '"${callable.name}":\n' 'Promises may only be returned for sass.compileAsync() and ' 'sass.compileStringAsync().'; } else { throw 'Invalid return value for custom function ' - '"${tuple.item1}": $result is not a sass.Value.'; + '"${callable.name}": $result is not a sass.Value.'; } - })); + }); + result.add(callable); } else { - result.add(AsyncBuiltInCallable.parsed(tuple.item1, tuple.item2, - (arguments) async { + late AsyncCallable callable; + callable = AsyncCallable.fromSignature(signature, (arguments) async { var result = (callback as Function)(toJSArray(arguments)); if (isPromise(result)) { result = await promiseToFuture(result as Promise); @@ -270,8 +256,9 @@ List _parseFunctions(Object? functions, {bool asynch = false}) { if (result is Value) return result; throw 'Invalid return value for custom function ' - '"${tuple.item1}": $result is not a sass.Value.'; - })); + '"${callable.name}": $result is not a sass.Value.'; + }); + result.add(callable); } }); return result; diff --git a/lib/src/node/legacy.dart b/lib/src/node/legacy.dart index e310a8de5..1fe86d11f 100644 --- a/lib/src/node/legacy.dart +++ b/lib/src/node/legacy.dart @@ -10,9 +10,7 @@ import 'dart:typed_data'; import 'package:js/js.dart'; import 'package:node_interop/js.dart'; import 'package:path/path.dart' as p; -import 'package:tuple/tuple.dart'; -import '../ast/sass.dart'; import '../callable.dart'; import '../compile.dart'; import '../compile_result.dart'; @@ -21,7 +19,6 @@ import '../importer/legacy_node.dart'; import '../io.dart'; import '../logger.dart'; import '../logger/node_to_dart.dart'; -import '../parse/scss.dart'; import '../syntax.dart'; import '../util/nullable.dart'; import '../utils.dart'; @@ -208,22 +205,12 @@ List _parseFunctions(RenderOptions options, DateTime start, var result = []; jsForEach(functions, (signature, callback) { - Tuple2 tuple; - try { - tuple = ScssParser(signature).parseSignature(requireParens: false); - } on SassFormatException catch (error, stackTrace) { - throwWithTrace( - SassFormatException( - 'Invalid signature "$signature": ${error.message}', error.span), - stackTrace); - } - var context = RenderContext(options: _contextOptions(options, start)); context.options.context = context; var fiber = options.fiber; if (fiber != null) { - result.add(BuiltInCallable.parsed(tuple.item1, tuple.item2, (arguments) { + result.add(Callable.fromSignature(signature, (arguments) { var currentFiber = fiber.current; var jsArguments = [ ...arguments.map(wrapValue), @@ -240,16 +227,15 @@ List _parseFunctions(RenderOptions options, DateTime start, // `Zone.current` in an inconsistent state. ? runZoned(() => fiber.yield()) : result); - })); + }, requireParens: false)); } else if (!asynch) { - result.add(BuiltInCallable.parsed( - tuple.item1, - tuple.item2, + result.add(Callable.fromSignature( + signature, (arguments) => unwrapValue((callback as JSFunction) - .apply(context, arguments.map(wrapValue).toList())))); + .apply(context, arguments.map(wrapValue).toList())), + requireParens: false)); } else { - result.add(AsyncBuiltInCallable.parsed(tuple.item1, tuple.item2, - (arguments) async { + result.add(AsyncCallable.fromSignature(signature, (arguments) async { var completer = Completer(); var jsArguments = [ ...arguments.map(wrapValue), @@ -258,7 +244,7 @@ List _parseFunctions(RenderOptions options, DateTime start, var result = (callback as JSFunction).apply(context, jsArguments); return unwrapValue( isUndefined(result) ? await completer.future : result); - })); + }, requireParens: false)); } }); return result; diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 20f12fcc6..295f753db 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -10,7 +10,11 @@ import 'package:source_span/source_span.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:string_scanner/string_scanner.dart'; import 'package:term_glyph/term_glyph.dart' as glyph; +import 'package:tuple/tuple.dart'; +import 'ast/sass.dart'; +import 'exception.dart'; +import 'parse/scss.dart'; import 'util/character.dart'; /// The URL used in stack traces when no source URL is available. @@ -469,3 +473,21 @@ extension IterableExtension on Iterable { return take(size); } } + +/// Parses a function signature of the format allowed by Node Sass's functions +/// option and returns its name and declaration. +/// +/// If [requireParens] is `false`, this allows parentheses to be omitted. +/// +/// Throws a [SassFormatException] if parsing fails. +Tuple2 parseSignature(String signature, + {bool requireParens = true}) { + try { + return ScssParser(signature).parseSignature(requireParens: requireParens); + } on SassFormatException catch (error, stackTrace) { + throwWithTrace( + SassFormatException( + 'Invalid signature "$signature": ${error.message}', error.span), + stackTrace); + } +} diff --git a/pkg/sass_api/CHANGELOG.md b/pkg/sass_api/CHANGELOG.md index 1155f85eb..8e8476613 100644 --- a/pkg/sass_api/CHANGELOG.md +++ b/pkg/sass_api/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.1.2 + +* No user-visible changes. + ## 4.1.1 * No user-visible changes. diff --git a/pkg/sass_api/pubspec.yaml b/pkg/sass_api/pubspec.yaml index 8a3e401ed..e4a69ccaa 100644 --- a/pkg/sass_api/pubspec.yaml +++ b/pkg/sass_api/pubspec.yaml @@ -2,7 +2,7 @@ name: sass_api # 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 # visitor interface(s). -version: 4.1.1 +version: 4.1.2 description: Additional APIs for Dart Sass. homepage: https://github.com/sass/dart-sass @@ -10,7 +10,7 @@ environment: sdk: ">=2.17.0 <3.0.0" dependencies: - sass: 1.56.1 + sass: 1.56.2 dev_dependencies: dartdoc: ^5.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index b20562378..36f86e43f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: sass -version: 1.56.1 +version: 1.56.2 description: A Sass implementation in Dart. homepage: https://github.com/sass/dart-sass