mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-22 05:41:14 +01:00
Fully support the return value for the Node render() functions (#165)
Closes #11
This commit is contained in:
parent
15d2064f33
commit
214a2af2e9
@ -19,7 +19,7 @@ import 'src/sync_package_resolver.dart';
|
||||
/// Throws a [SassException] if conversion fails.
|
||||
String compile(String path,
|
||||
{bool color: false, SyncPackageResolver packageResolver}) =>
|
||||
c.compile(path, color: color, packageResolver: packageResolver);
|
||||
c.compile(path, color: color, packageResolver: packageResolver).css;
|
||||
|
||||
/// Compiles [source] to CSS and returns the result.
|
||||
///
|
||||
@ -38,18 +38,20 @@ String compile(String path,
|
||||
///
|
||||
/// Throws a [SassException] if conversion fails.
|
||||
String compileString(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
SyncPackageResolver packageResolver,
|
||||
url}) =>
|
||||
c.compileString(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
packageResolver: packageResolver,
|
||||
url: url);
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
SyncPackageResolver packageResolver,
|
||||
url}) {
|
||||
var result = c.compileString(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
packageResolver: packageResolver,
|
||||
url: url);
|
||||
return result.css;
|
||||
}
|
||||
|
||||
/// Use [compile] instead.
|
||||
@Deprecated('Will be removed in 1.0.0')
|
||||
String render(String path,
|
||||
{bool color: false, SyncPackageResolver packageResolver}) =>
|
||||
c.compile(path, color: color, packageResolver: packageResolver);
|
||||
compile(path, color: color, packageResolver: packageResolver);
|
||||
|
@ -2,6 +2,8 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'dart:collection';
|
||||
|
||||
import 'ast/sass.dart';
|
||||
import 'io.dart';
|
||||
import 'sync_package_resolver.dart';
|
||||
@ -11,7 +13,7 @@ import 'visitor/serialize.dart';
|
||||
|
||||
/// Like [compile] in `lib/sass.dart`, but provides more options to support the
|
||||
/// node-sass compatible API.
|
||||
String compile(String path,
|
||||
CompileResult compile(String path,
|
||||
{bool indented,
|
||||
bool color: false,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -33,7 +35,7 @@ String compile(String path,
|
||||
|
||||
/// Like [compileString] in `lib/sass.dart`, but provides more options to support
|
||||
/// the node-sass compatible API.
|
||||
String compileString(String source,
|
||||
CompileResult compileString(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -46,11 +48,26 @@ String compileString(String source,
|
||||
var sassTree = indented
|
||||
? new Stylesheet.parseSass(source, url: url, color: color)
|
||||
: new Stylesheet.parseScss(source, url: url, color: color);
|
||||
var cssTree = evaluate(sassTree,
|
||||
var evaluateResult = evaluate(sassTree,
|
||||
color: color, packageResolver: packageResolver, loadPaths: loadPaths);
|
||||
return serialize(cssTree,
|
||||
var css = serialize(evaluateResult.stylesheet,
|
||||
style: style,
|
||||
useSpaces: useSpaces,
|
||||
indentWidth: indentWidth,
|
||||
lineFeed: lineFeed);
|
||||
|
||||
return new CompileResult(css, evaluateResult.includedUrls);
|
||||
}
|
||||
|
||||
/// The result of compiling a Sass document to CSS, along with metadata about
|
||||
/// the compilation process.
|
||||
class CompileResult {
|
||||
/// The compiled CSS.
|
||||
final String css;
|
||||
|
||||
/// The URLs that were loaded during the compilation, including the main
|
||||
/// file's.
|
||||
final Set<Uri> includedUrls;
|
||||
|
||||
CompileResult(this.css, this.includedUrls);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import 'node/render_error.dart';
|
||||
import 'node/render_options.dart';
|
||||
import 'node/render_result.dart';
|
||||
import 'node/utils.dart';
|
||||
import 'util/path.dart';
|
||||
import 'visitor/serialize.dart';
|
||||
|
||||
/// The entrypoint for Node.js.
|
||||
@ -71,14 +72,15 @@ RenderResult _renderSync(RenderOptions options) {
|
||||
/// Unlike [_render] and [_renderSync], this doesn't do any special handling for
|
||||
/// Dart exceptions.
|
||||
RenderResult _doRender(RenderOptions options) {
|
||||
String output;
|
||||
var start = new DateTime.now();
|
||||
CompileResult result;
|
||||
if (options.data != null) {
|
||||
if (options.file != null) {
|
||||
throw new ArgumentError(
|
||||
"options.data and options.file may not both be set.");
|
||||
}
|
||||
|
||||
output = compileString(options.data,
|
||||
result = compileString(options.data,
|
||||
loadPaths: options.includePaths,
|
||||
indented: options.indentedSyntax ?? false,
|
||||
style: _parseOutputStyle(options.outputStyle),
|
||||
@ -86,7 +88,7 @@ RenderResult _doRender(RenderOptions options) {
|
||||
indentWidth: _parseIndentWidth(options.indentWidth),
|
||||
lineFeed: _parseLineFeed(options.linefeed));
|
||||
} else if (options.file != null) {
|
||||
output = compile(options.file,
|
||||
result = compile(options.file,
|
||||
loadPaths: options.includePaths,
|
||||
indented: options.indentedSyntax,
|
||||
style: _parseOutputStyle(options.outputStyle),
|
||||
@ -96,8 +98,14 @@ RenderResult _doRender(RenderOptions options) {
|
||||
} else {
|
||||
throw new ArgumentError("Either options.data or options.file must be set.");
|
||||
}
|
||||
var end = new DateTime.now();
|
||||
|
||||
return newRenderResult(output);
|
||||
return newRenderResult(result.css,
|
||||
entry: options.file ?? 'data',
|
||||
start: start.millisecondsSinceEpoch,
|
||||
end: end.millisecondsSinceEpoch,
|
||||
duration: end.difference(start).inMilliseconds,
|
||||
includedFiles: result.includedUrls.map((url) => p.fromUri(url)).toList());
|
||||
}
|
||||
|
||||
/// Parse [style] into an [OutputStyle].
|
||||
|
@ -13,9 +13,39 @@ external _buffer(String source, String encoding);
|
||||
@anonymous
|
||||
class RenderResult {
|
||||
external Uint8List get css;
|
||||
external RenderResultStats get stats;
|
||||
|
||||
external factory RenderResult._({css});
|
||||
external factory RenderResult._({css, RenderResultStats stats});
|
||||
}
|
||||
|
||||
RenderResult newRenderResult(String css) =>
|
||||
new RenderResult._(css: _buffer(css, 'utf8'));
|
||||
@JS()
|
||||
@anonymous
|
||||
class RenderResultStats {
|
||||
external String get entry;
|
||||
external int get start;
|
||||
external int get end;
|
||||
external int get duration;
|
||||
external List<String> get includedFiles;
|
||||
|
||||
external factory RenderResultStats._(
|
||||
{String entry,
|
||||
int start,
|
||||
int end,
|
||||
int duration,
|
||||
List<String> includedFiles});
|
||||
}
|
||||
|
||||
RenderResult newRenderResult(String css,
|
||||
{String entry,
|
||||
int start,
|
||||
int end,
|
||||
int duration,
|
||||
List<String> includedFiles}) =>
|
||||
new RenderResult._(
|
||||
css: _buffer(css, 'utf8'),
|
||||
stats: new RenderResultStats._(
|
||||
entry: entry,
|
||||
start: start,
|
||||
end: end,
|
||||
duration: duration,
|
||||
includedFiles: includedFiles));
|
||||
|
@ -41,7 +41,7 @@ typedef _ScopeCallback(callback());
|
||||
/// If [color] is `true`, this will use terminal colors in warnings.
|
||||
///
|
||||
/// Throws a [SassRuntimeException] if evaluation fails.
|
||||
CssStylesheet evaluate(Stylesheet stylesheet,
|
||||
EvaluateResult evaluate(Stylesheet stylesheet,
|
||||
{Iterable<String> loadPaths,
|
||||
Environment environment,
|
||||
bool color: false,
|
||||
@ -125,7 +125,7 @@ class _EvaluateVisitor
|
||||
/// This is separate from [_importPaths] because multiple `@import` rules may
|
||||
/// import the same stylesheet, and we don't want to parse the same stylesheet
|
||||
/// multiple times.
|
||||
final _importedFiles = <String, Stylesheet>{};
|
||||
final _importedFiles = <Uri, Stylesheet>{};
|
||||
|
||||
final _activeImports = new Set<Uri>();
|
||||
|
||||
@ -203,10 +203,13 @@ class _EvaluateVisitor
|
||||
});
|
||||
}
|
||||
|
||||
CssStylesheet run(Stylesheet node) {
|
||||
if (node.span != null) _activeImports.add(node.span.sourceUrl);
|
||||
EvaluateResult run(Stylesheet node) {
|
||||
if (node.span?.sourceUrl != null) {
|
||||
_activeImports.add(node.span.sourceUrl);
|
||||
_importedFiles[node.span.sourceUrl] = node;
|
||||
}
|
||||
visitStylesheet(node);
|
||||
return _root;
|
||||
return new EvaluateResult(_root, new MapKeySet(_importedFiles));
|
||||
}
|
||||
|
||||
// ## Statements
|
||||
@ -637,7 +640,8 @@ class _EvaluateVisitor
|
||||
throw _exception("Can't find file to import.", import.span);
|
||||
}
|
||||
|
||||
return _importedFiles.putIfAbsent(path, () {
|
||||
var url = p.toUri(path);
|
||||
return _importedFiles.putIfAbsent(url, () {
|
||||
String contents;
|
||||
try {
|
||||
contents = readFile(path);
|
||||
@ -649,7 +653,6 @@ class _EvaluateVisitor
|
||||
throw _exception(error.message, import.span);
|
||||
}
|
||||
|
||||
var url = p.toUri(path);
|
||||
return p.extension(path) == '.sass'
|
||||
? new Stylesheet.parseSass(contents, url: url, color: _color)
|
||||
: new Stylesheet.parseScss(contents, url: url, color: _color);
|
||||
@ -1664,3 +1667,16 @@ class _EvaluateVisitor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of compiling a Sass document to a CSS tree, along with metadata
|
||||
/// about the compilation process.
|
||||
class EvaluateResult {
|
||||
/// The CSS syntax tree.
|
||||
final CssStylesheet stylesheet;
|
||||
|
||||
/// The URLs that were loaded during the compilation, including the main
|
||||
/// file's.
|
||||
final Set<Uri> includedUrls;
|
||||
|
||||
EvaluateResult(this.stylesheet, this.includedUrls);
|
||||
}
|
||||
|
@ -200,6 +200,57 @@ a {
|
||||
});
|
||||
});
|
||||
|
||||
group("the result object", () {
|
||||
test("includes the filename", () {
|
||||
var result = sass.renderSync(new RenderOptions(file: sassPath));
|
||||
expect(result.stats.entry, equals(sassPath));
|
||||
});
|
||||
|
||||
test("includes data without a filename", () {
|
||||
var result = sass.renderSync(new RenderOptions(data: 'a {b: c}'));
|
||||
expect(result.stats.entry, equals('data'));
|
||||
});
|
||||
|
||||
test("includes timing information", () {
|
||||
var result = sass.renderSync(new RenderOptions(file: sassPath));
|
||||
expect(result.stats.start, new isInstanceOf<int>());
|
||||
expect(result.stats.end, new isInstanceOf<int>());
|
||||
expect(result.stats.start, lessThan(result.stats.end));
|
||||
expect(result.stats.duration,
|
||||
equals(result.stats.end - result.stats.start));
|
||||
});
|
||||
|
||||
group("has includedFiles which", () {
|
||||
test("contains the root path if available", () {
|
||||
var result = sass.renderSync(new RenderOptions(file: sassPath));
|
||||
expect(result.stats.includedFiles, equals([sassPath]));
|
||||
});
|
||||
|
||||
test("doesn't contain the root path if it's not available", () {
|
||||
var result = sass.renderSync(new RenderOptions(data: 'a {b: c}'));
|
||||
expect(result.stats.includedFiles, isEmpty);
|
||||
});
|
||||
|
||||
test("contains imported paths", () async {
|
||||
var importerPath = p.join(sandbox, 'importer.scss');
|
||||
await writeTextFile(importerPath, '@import "test"');
|
||||
|
||||
var result = sass.renderSync(new RenderOptions(file: importerPath));
|
||||
expect(result.stats.includedFiles,
|
||||
unorderedEquals([importerPath, sassPath]));
|
||||
});
|
||||
|
||||
test("only contains each path once", () async {
|
||||
var importerPath = p.join(sandbox, 'importer.scss');
|
||||
await writeTextFile(importerPath, '@import "test"; @import "test";');
|
||||
|
||||
var result = sass.renderSync(new RenderOptions(file: importerPath));
|
||||
expect(result.stats.includedFiles,
|
||||
unorderedEquals([importerPath, sassPath]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("throws an error that", () {
|
||||
setUp(() => writeTextFile(sassPath, 'a {b: }'));
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user