mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-26 20:24:42 +01:00
Expose an API for defining custom Dart functions
This commit is contained in:
parent
aa3c765b10
commit
8988c3c7fa
@ -4,12 +4,15 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'src/callable.dart';
|
||||
import 'src/compile.dart' as c;
|
||||
import 'src/exception.dart';
|
||||
import 'src/importer.dart';
|
||||
import 'src/sync_package_resolver.dart';
|
||||
|
||||
export 'src/callable.dart' show Callable, AsyncCallable;
|
||||
export 'src/importer.dart';
|
||||
export 'src/value.dart' hide SassNull;
|
||||
|
||||
/// Loads the Sass file at [path], compiles it to CSS, and returns the result.
|
||||
///
|
||||
@ -30,17 +33,23 @@ export 'src/importer.dart';
|
||||
///
|
||||
/// [SyncPackageResolver]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html
|
||||
///
|
||||
/// Dart functions that can be called from Sass may be passed using [functions].
|
||||
/// Each [Callable] defines a top-level function that will be invoked when the
|
||||
/// given name is called from Sass.
|
||||
///
|
||||
/// Throws a [SassException] if conversion fails.
|
||||
String compile(String path,
|
||||
{bool color: false,
|
||||
Iterable<Importer> importers,
|
||||
Iterable<String> loadPaths,
|
||||
SyncPackageResolver packageResolver}) {
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<Callable> functions}) {
|
||||
var result = c.compile(path,
|
||||
color: color,
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver);
|
||||
packageResolver: packageResolver,
|
||||
functions: functions);
|
||||
return result.css;
|
||||
}
|
||||
|
||||
@ -65,6 +74,10 @@ String compile(String path,
|
||||
///
|
||||
/// [SyncPackageResolver]: https://www.dartdocs.org/documentation/package_resolver/latest/package_resolver/SyncPackageResolver-class.html
|
||||
///
|
||||
/// Dart functions that can be called from Sass may be passed using [functions].
|
||||
/// Each [Callable] defines a top-level function that will be invoked when the
|
||||
/// given name is called from Sass.
|
||||
///
|
||||
/// The [url] indicates the location from which [source] was loaded. It may be a
|
||||
/// [String] or a [Uri]. If [importer] is passed, [url] must be passed as well
|
||||
/// and `importer.load(url)` should return `source`.
|
||||
@ -74,16 +87,18 @@ String compileString(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Iterable<Importer> importers,
|
||||
Iterable<String> loadPaths,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Iterable<Callable> functions,
|
||||
Importer importer,
|
||||
url}) {
|
||||
var result = c.compileString(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver,
|
||||
loadPaths: loadPaths,
|
||||
functions: functions,
|
||||
importer: importer,
|
||||
url: url);
|
||||
return result.css;
|
||||
@ -97,13 +112,15 @@ String compileString(String source,
|
||||
Future<String> compileAsync(String path,
|
||||
{bool color: false,
|
||||
Iterable<AsyncImporter> importers,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
SyncPackageResolver packageResolver}) async {
|
||||
Iterable<AsyncCallable> functions}) async {
|
||||
var result = await c.compileAsync(path,
|
||||
color: color,
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver);
|
||||
packageResolver: packageResolver,
|
||||
functions: functions);
|
||||
return result.css;
|
||||
}
|
||||
|
||||
@ -116,16 +133,18 @@ Future<String> compileStringAsync(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Iterable<AsyncImporter> importers,
|
||||
Iterable<String> loadPaths,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Iterable<AsyncCallable> functions,
|
||||
AsyncImporter importer,
|
||||
url}) async {
|
||||
var result = await c.compileStringAsync(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver,
|
||||
loadPaths: loadPaths,
|
||||
functions: functions,
|
||||
importer: importer,
|
||||
url: url);
|
||||
return result.css;
|
||||
|
@ -3,6 +3,8 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'callable/async.dart';
|
||||
import 'callable/built_in.dart';
|
||||
import 'value.dart';
|
||||
|
||||
export 'callable/async.dart';
|
||||
export 'callable/async_built_in.dart';
|
||||
@ -17,4 +19,7 @@ export 'callable/user_defined.dart';
|
||||
/// usable in asynchronous contexts. [Callable]s are usable with both the
|
||||
/// synchronous and asynchronous `compile()` functions, and as such should be
|
||||
/// used in preference to [AsyncCallable]s if possible.
|
||||
abstract class Callable extends AsyncCallable {}
|
||||
abstract class Callable extends AsyncCallable {
|
||||
factory Callable(String name, String arguments,
|
||||
Value callback(List<Value> arguments)) = BuiltInCallable;
|
||||
}
|
||||
|
@ -2,6 +2,11 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import '../value.dart';
|
||||
import 'async_built_in.dart';
|
||||
|
||||
/// An interface for objects, such as functions and mixins, that can be invoked
|
||||
/// from Sass by passing in arguments.
|
||||
///
|
||||
@ -11,4 +16,7 @@
|
||||
abstract class AsyncCallable {
|
||||
/// The callable's name.
|
||||
String get name;
|
||||
|
||||
factory AsyncCallable(String name, String arguments,
|
||||
FutureOr<Value> callback(List<Value> arguments)) = AsyncBuiltInCallable;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'ast/sass.dart';
|
||||
import 'callable.dart';
|
||||
import 'importer.dart';
|
||||
import 'importer/node.dart';
|
||||
import 'io.dart';
|
||||
@ -23,6 +24,7 @@ CompileResult compile(String path,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Iterable<Callable> functions,
|
||||
OutputStyle style,
|
||||
bool useSpaces: true,
|
||||
int indentWidth,
|
||||
@ -30,6 +32,7 @@ CompileResult compile(String path,
|
||||
compileString(readFile(path),
|
||||
indented: indented ?? p.extension(path) == '.sass',
|
||||
color: color,
|
||||
functions: functions,
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
packageResolver: packageResolver,
|
||||
@ -51,6 +54,7 @@ CompileResult compileString(String source,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Importer importer,
|
||||
Iterable<Callable> functions,
|
||||
OutputStyle style,
|
||||
bool useSpaces: true,
|
||||
int indentWidth,
|
||||
@ -65,6 +69,7 @@ CompileResult compileString(String source,
|
||||
..addAll(_toImporters(loadPaths, packageResolver)),
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color);
|
||||
var css = serialize(evaluateResult.stylesheet,
|
||||
style: style,
|
||||
@ -84,6 +89,7 @@ Future<CompileResult> compileAsync(String path,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Iterable<AsyncCallable> functions,
|
||||
OutputStyle style,
|
||||
bool useSpaces: true,
|
||||
int indentWidth,
|
||||
@ -96,6 +102,7 @@ Future<CompileResult> compileAsync(String path,
|
||||
packageResolver: packageResolver,
|
||||
loadPaths: loadPaths,
|
||||
importer: new FilesystemImporter('.'),
|
||||
functions: functions,
|
||||
style: style,
|
||||
useSpaces: useSpaces,
|
||||
indentWidth: indentWidth,
|
||||
@ -112,6 +119,7 @@ Future<CompileResult> compileStringAsync(String source,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
AsyncImporter importer,
|
||||
Iterable<AsyncCallable> functions,
|
||||
OutputStyle style,
|
||||
bool useSpaces: true,
|
||||
int indentWidth,
|
||||
@ -126,6 +134,7 @@ Future<CompileResult> compileStringAsync(String source,
|
||||
..addAll(_toImporters(loadPaths, packageResolver)),
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color);
|
||||
var css = serialize(evaluateResult.stylesheet,
|
||||
style: style,
|
||||
|
@ -56,13 +56,13 @@ Future<EvaluateResult> evaluateAsync(Stylesheet stylesheet,
|
||||
{Iterable<AsyncImporter> importers,
|
||||
NodeImporter nodeImporter,
|
||||
AsyncImporter importer,
|
||||
AsyncEnvironment environment,
|
||||
Iterable<AsyncCallable> functions,
|
||||
bool color: false}) =>
|
||||
new _EvaluateVisitor(
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
environment: environment,
|
||||
functions: functions,
|
||||
color: color)
|
||||
.run(stylesheet);
|
||||
|
||||
@ -82,7 +82,7 @@ class _EvaluateVisitor
|
||||
final bool _color;
|
||||
|
||||
/// The current lexical environment.
|
||||
AsyncEnvironment _environment;
|
||||
var _environment = new AsyncEnvironment();
|
||||
|
||||
/// The importer that's currently being used to resolve relative imports.
|
||||
///
|
||||
@ -168,12 +168,11 @@ class _EvaluateVisitor
|
||||
{Iterable<AsyncImporter> importers,
|
||||
NodeImporter nodeImporter,
|
||||
AsyncImporter importer,
|
||||
AsyncEnvironment environment,
|
||||
Iterable<AsyncCallable> functions,
|
||||
bool color: false})
|
||||
: _importers = importers == null ? const [] : importers.toList(),
|
||||
_importer = importer ?? Importer.noOp,
|
||||
_nodeImporter = nodeImporter,
|
||||
_environment = environment ?? new AsyncEnvironment(),
|
||||
_color = color {
|
||||
_environment.setFunction(
|
||||
new BuiltInCallable("global-variable-exists", r"$name", (arguments) {
|
||||
@ -258,6 +257,10 @@ class _EvaluateVisitor
|
||||
"This is probably caused by a bug in a Sass plugin.");
|
||||
}
|
||||
}));
|
||||
|
||||
for (var function in functions ?? const <AsyncCallable>[]) {
|
||||
_environment.setFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
Future<EvaluateResult> run(Stylesheet node) async {
|
||||
@ -1394,8 +1397,19 @@ class _EvaluateVisitor
|
||||
positional.add(argumentList);
|
||||
}
|
||||
|
||||
var result = await _addExceptionSpanAsync(
|
||||
span, () async => await callback(positional));
|
||||
Value result;
|
||||
try {
|
||||
result = await callback(positional);
|
||||
if (result == null) throw "Custom functions may not return Dart's null.";
|
||||
} catch (error, stackTrace) {
|
||||
String message;
|
||||
try {
|
||||
message = error.message as String;
|
||||
} catch (_) {
|
||||
message = error.toString();
|
||||
}
|
||||
throw _exception(message, span);
|
||||
}
|
||||
_callableSpan = oldCallableSpan;
|
||||
|
||||
if (argumentList == null) return result;
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: d443af264c677b3e99b5b32511898d92b9b41291
|
||||
// Checksum: 4d48952f8cd737f998fc1f67e801f4ea0ab69671
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
@ -60,13 +60,13 @@ EvaluateResult evaluate(Stylesheet stylesheet,
|
||||
{Iterable<Importer> importers,
|
||||
NodeImporter nodeImporter,
|
||||
Importer importer,
|
||||
Environment environment,
|
||||
Iterable<Callable> functions,
|
||||
bool color: false}) =>
|
||||
new _EvaluateVisitor(
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
environment: environment,
|
||||
functions: functions,
|
||||
color: color)
|
||||
.run(stylesheet);
|
||||
|
||||
@ -84,7 +84,7 @@ class _EvaluateVisitor
|
||||
final bool _color;
|
||||
|
||||
/// The current lexical environment.
|
||||
Environment _environment;
|
||||
var _environment = new Environment();
|
||||
|
||||
/// The importer that's currently being used to resolve relative imports.
|
||||
///
|
||||
@ -170,12 +170,11 @@ class _EvaluateVisitor
|
||||
{Iterable<Importer> importers,
|
||||
NodeImporter nodeImporter,
|
||||
Importer importer,
|
||||
Environment environment,
|
||||
Iterable<Callable> functions,
|
||||
bool color: false})
|
||||
: _importers = importers == null ? const [] : importers.toList(),
|
||||
_importer = importer ?? Importer.noOp,
|
||||
_nodeImporter = nodeImporter,
|
||||
_environment = environment ?? new Environment(),
|
||||
_color = color {
|
||||
_environment.setFunction(
|
||||
new BuiltInCallable("global-variable-exists", r"$name", (arguments) {
|
||||
@ -260,6 +259,10 @@ class _EvaluateVisitor
|
||||
"This is probably caused by a bug in a Sass plugin.");
|
||||
}
|
||||
}));
|
||||
|
||||
for (var function in functions ?? const <Callable>[]) {
|
||||
_environment.setFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
EvaluateResult run(Stylesheet node) {
|
||||
@ -1372,7 +1375,19 @@ class _EvaluateVisitor
|
||||
positional.add(argumentList);
|
||||
}
|
||||
|
||||
var result = _addExceptionSpan(span, () => callback(positional));
|
||||
Value result;
|
||||
try {
|
||||
result = callback(positional);
|
||||
if (result == null) throw "Custom functions may not return Dart's null.";
|
||||
} catch (error, stackTrace) {
|
||||
String message;
|
||||
try {
|
||||
message = error.message as String;
|
||||
} catch (_) {
|
||||
message = error.toString();
|
||||
}
|
||||
throw _exception(message, span);
|
||||
}
|
||||
_callableSpan = oldCallableSpan;
|
||||
|
||||
if (argumentList == null) return result;
|
||||
|
Loading…
Reference in New Issue
Block a user