Change CompileResult.includedFiles to CompileResult.includedUrls

Rather than constructing this explicitly to match Node Sass's API, we
construct it with canonical URLs and convert it into the format
expected by the Node Sass API (a mix of paths and URLs) in the
compatibility layer.
This commit is contained in:
Natalie Weizenbaum 2021-07-13 17:15:22 -07:00
parent 0e42a09fdb
commit 43b69e60a0
15 changed files with 420 additions and 191 deletions

View File

@ -1,3 +1,22 @@
## 1.36.0
### Dart API
* Added `compileToResult()`, `compileStringToResult()`,
`compileToResultAsync()`, and `compileStringToResultAsync()` methods. These
are intended to replace the existing `compile*()` methods, which are now
deprecated. Rather than returning a simple string, these return a
`CompileResult` object, which will allow us to add additional information
about the compilation without having to introduce further deprecations.
* Instead of passing a `sourceMaps` callback to `compile*()`, pass
`sourceMaps: true` to `compile*ToResult()` and access
`CompileResult.sourceMap`.
* The `CompileResult` object exposes a `includedUrls` object which lists the
canonical URLs accessed during a compilation. This information was
previously unavailable except through the JS API.
## 1.35.2
* **Potentially breaking bug fix**: Properly throw an error for Unicode ranges

View File

@ -11,6 +11,7 @@ import 'package:source_maps/source_maps.dart';
import 'src/async_import_cache.dart';
import 'src/callable.dart';
import 'src/compile.dart' as c;
import 'src/compile_result.dart';
import 'src/exception.dart';
import 'src/import_cache.dart';
import 'src/importer.dart';
@ -20,6 +21,7 @@ import 'src/util/nullable.dart';
import 'src/visitor/serialize.dart';
export 'src/callable.dart' show Callable, AsyncCallable;
export 'src/compile_result.dart';
export 'src/exception.dart' show SassException;
export 'src/importer.dart';
export 'src/logger.dart';
@ -28,7 +30,9 @@ export 'src/value.dart';
export 'src/visitor/serialize.dart' show OutputStyle;
export 'src/warn.dart' show warn;
/// Loads the Sass file at [path], compiles it to CSS, and returns the result.
/// Loads the Sass file at [path], compiles it to CSS, and returns a
/// [CompileResult] containing the CSS and additional metadata about the
/// compilation.
///
/// If [color] is `true`, this will use terminal colors in warnings. It's
/// ignored if [logger] is passed.
@ -67,12 +71,13 @@ export 'src/warn.dart' show warn;
/// times, further warnings for that feature are silenced. If [verbose] is true,
/// all deprecation warnings are printed instead.
///
/// If [sourceMap] is passed, it's passed a [SingleMapping] that indicates which
/// sections of the source file(s) correspond to which in the resulting CSS.
/// It's called immediately before this method returns, and only if compilation
/// succeeds. Note that [SingleMapping.targetUrl] will always be `null`. Users
/// using the [SourceMap] API should be sure to add the [`source_maps`][]
/// package to their pubspec.
/// If [sourceMap] is `true`, [CompileResult.sourceMap] will be set to a
/// [SingleMapping] that indicates which sections of the source file(s)
/// correspond to which in the resulting CSS. [SingleMapping.targetUrl] will be
/// `null`. It's up to the caller to save this mapping to disk and add a source
/// map comment to [CompileResult.css] pointing to it. Users using the
/// [SourceMap] API should be sure to add the [`source_maps`][] package to their
/// pubspec.
///
/// [`source_maps`]: https://pub.dartlang.org/packages/source_maps
///
@ -83,46 +88,35 @@ export 'src/warn.dart' show warn;
///
/// [byte-order mark]: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
///
/// This parameter is meant to be used as an out parameter, so that users who
/// want access to the source map can get it. For example:
///
/// ```dart
/// SingleMapping sourceMap;
/// var css = compile(sassPath, sourceMap: (map) => sourceMap = map);
/// ```
///
/// Throws a [SassException] if conversion fails.
String compile(String path,
{bool color = false,
Logger? logger,
Iterable<Importer>? importers,
Iterable<String>? loadPaths,
PackageConfig? packageConfig,
Iterable<Callable>? functions,
OutputStyle? style,
bool quietDeps = false,
bool verbose = false,
void sourceMap(SingleMapping map)?,
bool charset = true}) {
logger ??= Logger.stderr(color: color);
var result = c.compile(path,
logger: logger,
importCache: ImportCache(
importers: importers,
logger: logger,
loadPaths: loadPaths,
packageConfig: packageConfig),
functions: functions,
style: style,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap != null,
charset: charset);
result.sourceMap.andThen(sourceMap);
return result.css;
}
CompileResult compileToResult(String path,
{bool color = false,
Logger? logger,
Iterable<Importer>? importers,
Iterable<String>? loadPaths,
PackageConfig? packageConfig,
Iterable<Callable>? functions,
OutputStyle? style,
bool quietDeps = false,
bool verbose = false,
bool sourceMap = false,
bool charset = true}) =>
c.compile(path,
logger: logger,
importCache: ImportCache(
importers: importers,
logger: logger ?? Logger.stderr(color: color),
loadPaths: loadPaths,
packageConfig: packageConfig),
functions: functions,
style: style,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap,
charset: charset);
/// Compiles [source] to CSS and returns the result.
/// Compiles [source] to CSS and returns a [CompileResult] containing the CSS
/// and additional metadata about the compilation..
///
/// This parses the stylesheet as [syntax], which defaults to [Syntax.scss].
///
@ -167,12 +161,13 @@ String compile(String path,
/// times, further warnings for that feature are silenced. If [verbose] is true,
/// all deprecation warnings are printed instead.
///
/// If [sourceMap] is passed, it's passed a [SingleMapping] that indicates which
/// sections of the source file(s) correspond to which in the resulting CSS.
/// It's called immediately before this method returns, and only if compilation
/// succeeds. Note that [SingleMapping.targetUrl] will always be `null`. Users
/// using the [SourceMap] API should be sure to add the [`source_maps`][]
/// package to their pubspec.
/// If [sourceMap] is `true`, [CompileResult.sourceMap] will be set to a
/// [SingleMapping] that indicates which sections of the source file(s)
/// correspond to which in the resulting CSS. [SingleMapping.targetUrl] will be
/// `null`. It's up to the caller to save this mapping to disk and add a source
/// map comment to [CompileResult.css] pointing to it. Users using the
/// [SourceMap] API should be sure to add the [`source_maps`][] package to their
/// pubspec.
///
/// [`source_maps`]: https://pub.dartlang.org/packages/source_maps
///
@ -183,6 +178,117 @@ String compile(String path,
///
/// [byte-order mark]: https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
///
/// Throws a [SassException] if conversion fails.
CompileResult compileStringToResult(String source,
{Syntax? syntax,
bool color = false,
Logger? logger,
Iterable<Importer>? importers,
PackageConfig? packageConfig,
Iterable<String>? loadPaths,
Iterable<Callable>? functions,
OutputStyle? style,
Importer? importer,
Object? url,
bool quietDeps = false,
bool verbose = false,
bool sourceMap = false,
bool charset = true}) =>
c.compileString(source,
syntax: syntax,
logger: logger,
importCache: ImportCache(
importers: importers,
logger: logger ?? Logger.stderr(color: color),
packageConfig: packageConfig,
loadPaths: loadPaths),
functions: functions,
style: style,
importer: importer,
url: url,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap,
charset: charset);
/// Like [compileToResult], except it runs asynchronously.
///
/// Running asynchronously allows this to take [AsyncImporter]s rather than
/// synchronous [Importer]s. However, running asynchronously is also somewhat
/// slower, so [compileToResult] should be preferred if possible.
Future<CompileResult> compileToResultAsync(String path,
{bool color = false,
Logger? logger,
Iterable<AsyncImporter>? importers,
PackageConfig? packageConfig,
Iterable<String>? loadPaths,
Iterable<AsyncCallable>? functions,
OutputStyle? style,
bool quietDeps = false,
bool verbose = false,
bool sourceMap = false}) =>
c.compileAsync(path,
logger: logger,
importCache: AsyncImportCache(
importers: importers,
logger: logger ?? Logger.stderr(color: color),
loadPaths: loadPaths,
packageConfig: packageConfig),
functions: functions,
style: style,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap);
/// Like [compileStringToResult], except it runs asynchronously.
///
/// Running asynchronously allows this to take [AsyncImporter]s rather than
/// synchronous [Importer]s. However, running asynchronously is also somewhat
/// slower, so [compileStringToResult] should be preferred if possible.
Future<CompileResult> compileStringToResultAsync(String source,
{Syntax? syntax,
bool color = false,
Logger? logger,
Iterable<AsyncImporter>? importers,
PackageConfig? packageConfig,
Iterable<String>? loadPaths,
Iterable<AsyncCallable>? functions,
OutputStyle? style,
AsyncImporter? importer,
Object? url,
bool quietDeps = false,
bool verbose = false,
bool sourceMap = false,
bool charset = true}) =>
c.compileStringAsync(source,
syntax: syntax,
logger: logger,
importCache: AsyncImportCache(
importers: importers,
logger: logger ?? Logger.stderr(color: color),
packageConfig: packageConfig,
loadPaths: loadPaths),
functions: functions,
style: style,
importer: importer,
url: url,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap,
charset: charset);
/// Like [compileToResult], but returns [CompileResult.css] rather than
/// returning [CompileResult] directly.
///
/// If [sourceMap] is passed, it's passed a [SingleMapping] that indicates which
/// sections of the source file(s) correspond to which in the resulting CSS.
/// It's called immediately before this method returns, and only if compilation
/// succeeds. Note that [SingleMapping.targetUrl] will always be `null`. Users
/// using the [SourceMap] API should be sure to add the [`source_maps`][]
/// package to their pubspec.
///
/// [`source_maps`]: https://pub.dartlang.org/packages/source_maps
///
/// This parameter is meant to be used as an out parameter, so that users who
/// want access to the source map can get it. For example:
///
@ -190,9 +296,58 @@ String compile(String path,
/// SingleMapping sourceMap;
/// var css = compile(sassPath, sourceMap: (map) => sourceMap = map);
/// ```
@Deprecated("Use compileToResult() instead.")
String compile(
String path,
{bool color = false,
Logger? logger,
Iterable<Importer>? importers,
Iterable<String>? loadPaths,
PackageConfig? packageConfig,
Iterable<Callable>? functions,
OutputStyle? style,
bool quietDeps = false,
bool verbose = false,
@Deprecated("Use CompileResult.sourceMap from compileToResult() instead.")
void sourceMap(SingleMapping map)?,
bool charset = true}) {
var result = compileToResult(path,
logger: logger,
importers: importers,
loadPaths: loadPaths,
packageConfig: packageConfig,
functions: functions,
style: style,
quietDeps: quietDeps,
verbose: verbose,
sourceMap: sourceMap != null,
charset: charset);
result.sourceMap.andThen(sourceMap);
return result.css;
}
/// Like [compileStringToResult], but returns [CompileResult.css] rather than
/// returning [CompileResult] directly.
///
/// Throws a [SassException] if conversion fails.
String compileString(String source,
/// If [sourceMap] is passed, it's passed a [SingleMapping] that indicates which
/// sections of the source file(s) correspond to which in the resulting CSS.
/// It's called immediately before this method returns, and only if compilation
/// succeeds. Note that [SingleMapping.targetUrl] will always be `null`. Users
/// using the [SourceMap] API should be sure to add the [`source_maps`][]
/// package to their pubspec.
///
/// [`source_maps`]: https://pub.dartlang.org/packages/source_maps
///
/// This parameter is meant to be used as an out parameter, so that users who
/// want access to the source map can get it. For example:
///
/// ```dart
/// SingleMapping sourceMap;
/// var css = compileString(sass, sourceMap: (map) => sourceMap = map);
/// ```
@Deprecated("Use compileStringToResult() instead.")
String compileString(
String source,
{Syntax? syntax,
bool color = false,
Logger? logger,
@ -205,18 +360,17 @@ String compileString(String source,
Object? url,
bool quietDeps = false,
bool verbose = false,
void sourceMap(SingleMapping map)?,
@Deprecated("Use CompileResult.sourceMap from compileStringToResult() instead.")
void sourceMap(SingleMapping map)?,
bool charset = true,
@Deprecated("Use syntax instead.") bool indented = false}) {
logger ??= Logger.stderr(color: color);
var result = c.compileString(source,
@Deprecated("Use syntax instead.")
bool indented = false}) {
var result = compileStringToResult(source,
syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss),
logger: logger,
importCache: ImportCache(
importers: importers,
logger: logger,
packageConfig: packageConfig,
loadPaths: loadPaths),
importers: importers,
packageConfig: packageConfig,
loadPaths: loadPaths,
functions: functions,
style: style,
importer: importer,
@ -234,7 +388,9 @@ String compileString(String source,
/// Running asynchronously allows this to take [AsyncImporter]s rather than
/// synchronous [Importer]s. However, running asynchronously is also somewhat
/// slower, so [compile] should be preferred if possible.
Future<String> compileAsync(String path,
@Deprecated("Use compileToResultAsync() instead.")
Future<String> compileAsync(
String path,
{bool color = false,
Logger? logger,
Iterable<AsyncImporter>? importers,
@ -244,15 +400,13 @@ Future<String> compileAsync(String path,
OutputStyle? style,
bool quietDeps = false,
bool verbose = false,
void sourceMap(SingleMapping map)?}) async {
logger ??= Logger.stderr(color: color);
var result = await c.compileAsync(path,
@Deprecated("Use CompileResult.sourceMap from compileToResultAsync() instead.")
void sourceMap(SingleMapping map)?}) async {
var result = await compileToResultAsync(path,
logger: logger,
importCache: AsyncImportCache(
importers: importers,
logger: logger,
loadPaths: loadPaths,
packageConfig: packageConfig),
importers: importers,
loadPaths: loadPaths,
packageConfig: packageConfig,
functions: functions,
style: style,
quietDeps: quietDeps,
@ -267,7 +421,9 @@ Future<String> compileAsync(String path,
/// Running asynchronously allows this to take [AsyncImporter]s rather than
/// synchronous [Importer]s. However, running asynchronously is also somewhat
/// slower, so [compileString] should be preferred if possible.
Future<String> compileStringAsync(String source,
@Deprecated("Use compileStringToResultAsync() instead.")
Future<String> compileStringAsync(
String source,
{Syntax? syntax,
bool color = false,
Logger? logger,
@ -280,18 +436,17 @@ Future<String> compileStringAsync(String source,
Object? url,
bool quietDeps = false,
bool verbose = false,
void sourceMap(SingleMapping map)?,
@Deprecated("Use CompileResult.sourceMap from compileStringToResultAsync() instead.")
void sourceMap(SingleMapping map)?,
bool charset = true,
@Deprecated("Use syntax instead.") bool indented = false}) async {
logger ??= Logger.stderr(color: color);
var result = await c.compileStringAsync(source,
@Deprecated("Use syntax instead.")
bool indented = false}) async {
var result = await compileStringToResultAsync(source,
syntax: syntax ?? (indented ? Syntax.sass : Syntax.scss),
logger: logger,
importCache: AsyncImportCache(
importers: importers,
logger: logger,
packageConfig: packageConfig,
loadPaths: loadPaths),
importers: importers,
packageConfig: packageConfig,
loadPaths: loadPaths,
functions: functions,
style: style,
importer: importer,

View File

@ -5,11 +5,11 @@
import 'dart:convert';
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import 'ast/sass.dart';
import 'async_import_cache.dart';
import 'callable.dart';
import 'compile_result.dart';
import 'importer.dart';
import 'importer/node.dart';
import 'io.dart';
@ -171,30 +171,3 @@ Future<CompileResult> _compileStylesheet(
return CompileResult(evaluateResult, serializeResult);
}
/// The result of compiling a Sass document to CSS, along with metadata about
/// the compilation process.
class CompileResult {
/// The result of evaluating the source file.
final EvaluateResult _evaluate;
/// The result of serializing the CSS AST to CSS text.
final SerializeResult _serialize;
/// The compiled CSS.
String get css => _serialize.css;
/// The source map indicating how the source files map to [css].
///
/// This is `null` if source mapping was disabled for this compilation.
SingleMapping? get sourceMap => _serialize.sourceMap;
/// The set that will eventually populate the JS API's
/// `result.stats.includedFiles` field.
///
/// For filesystem imports, this contains the import path. For all other
/// imports, it contains the URL passed to the `@import`.
Set<String> get includedFiles => _evaluate.includedFiles;
CompileResult(this._evaluate, this._serialize);
}

View File

@ -5,22 +5,20 @@
// DO NOT EDIT. This file was generated from async_compile.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: 8e813f2ead6e78899ce820e279983278809a7ea5
// Checksum: 1a1251aa9f7312612a64760f59803568bd09a07c
//
// ignore_for_file: unused_import
import 'async_compile.dart';
export 'async_compile.dart';
import 'dart:convert';
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import 'package:source_span/source_span.dart';
import 'ast/sass.dart';
import 'import_cache.dart';
import 'callable.dart';
import 'compile_result.dart';
import 'importer.dart';
import 'importer/node.dart';
import 'io.dart';

View File

@ -0,0 +1,35 @@
// 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 'package:meta/meta.dart';
import 'package:source_maps/source_maps.dart';
import 'visitor/async_evaluate.dart';
import 'visitor/serialize.dart';
/// The result of compiling a Sass document to CSS, along with metadata about
/// the compilation process.
@sealed
class CompileResult {
/// The result of evaluating the source file.
final EvaluateResult _evaluate;
/// The result of serializing the CSS AST to CSS text.
final SerializeResult _serialize;
/// The compiled CSS.
String get css => _serialize.css;
/// The source map indicating how the source files map to [css].
///
/// This is `null` if source mapping was disabled for this compilation.
SingleMapping? get sourceMap => _serialize.sourceMap;
/// The canonical URLs of all stylesheets loaded during compilation.
Set<Uri> get includedUrls => _evaluate.includedUrls;
/// @nodoc
@internal
CompileResult(this._evaluate, this._serialize);
}

View File

@ -9,6 +9,7 @@ import 'package:source_maps/source_maps.dart';
import '../async_import_cache.dart';
import '../compile.dart';
import '../compile_result.dart';
import '../exception.dart';
import '../importer/filesystem.dart';
import '../io.dart';

View File

@ -27,9 +27,8 @@ import '../utils.dart';
/// imported by a different importer.
///
/// * Importers can return file paths rather than the contents of the imported
/// file. These paths are made absolute before they're included in
/// [EvaluateResult.includedFiles] or passed as the previous "URL" to other
/// importers.
/// file. These paths are made absolute before they'repassed as the previous
/// "URL" to other importers.
///
/// * The working directory is always implicitly an include path.
///

View File

@ -15,6 +15,7 @@ import 'package:tuple/tuple.dart';
import 'ast/sass.dart';
import 'callable.dart';
import 'compile.dart';
import 'compile_result.dart';
import 'exception.dart';
import 'io.dart';
import 'importer/node.dart';
@ -408,7 +409,10 @@ RenderResult _newRenderResult(
start: start.millisecondsSinceEpoch,
end: end.millisecondsSinceEpoch,
duration: end.difference(start).inMilliseconds,
includedFiles: result.includedFiles.toList()));
includedFiles: [
for (var url in result.includedUrls)
if (url.scheme == 'file') p.fromUri(url) else url.toString()
]));
}
/// Returns whether source maps are enabled by [options].

View File

@ -216,12 +216,8 @@ class _EvaluateVisitor
/// Whether we're currently building the output of a `@keyframes` rule.
var _inKeyframes = false;
/// The set that will eventually populate the JS API's
/// `result.stats.includedFiles` field.
///
/// For filesystem imports, this contains the import path. For all other
/// imports, it contains the URL passed to the `@import`.
final _includedFiles = <String>{};
/// The canonical URLs of all stylesheets loaded during compilation.
final _includedUrls = <Uri>{};
/// A map from canonical URLs for modules (or imported files) that are
/// currently being evaluated to AST nodes whose spans indicate the original
@ -508,18 +504,12 @@ class _EvaluateVisitor
var url = node.span.sourceUrl;
if (url != null) {
_activeModules[url] = null;
if (_asNodeSass) {
if (url.scheme == 'file') {
_includedFiles.add(p.fromUri(url));
} else if (url.toString() != 'stdin') {
_includedFiles.add(url.toString());
}
}
if (!(_asNodeSass && url.toString() == 'stdin')) _includedUrls.add(url);
}
var module = await _execute(importer, node);
return EvaluateResult(_combineCss(module), _includedFiles);
return EvaluateResult(_combineCss(module), _includedUrls);
});
}
@ -1577,13 +1567,17 @@ class _EvaluateVisitor
tuple.item1, tuple.item2,
originalUrl: tuple.item3, quiet: _quietDeps && isDependency);
if (stylesheet != null) {
_includedUrls.add(tuple.item2);
return _LoadedStylesheet(stylesheet,
importer: tuple.item1, isDependency: isDependency);
}
}
} else {
var result = await _importLikeNode(url, forImport);
if (result != null) return result;
if (result != null) {
result.stylesheet.span.sourceUrl.andThen(_includedUrls.add);
return result;
}
}
if (url.startsWith('package:') && isNode) {
@ -1629,8 +1623,6 @@ class _EvaluateVisitor
var contents = result.item1;
var url = result.item2;
_includedFiles.add(url.startsWith('file:') ? p.fromUri(url) : url);
return _LoadedStylesheet(
Stylesheet.parse(contents,
url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss,
@ -3316,14 +3308,10 @@ class EvaluateResult {
/// The CSS syntax tree.
final CssStylesheet stylesheet;
/// The set that will eventually populate the JS API's
/// `result.stats.includedFiles` field.
///
/// For filesystem imports, this contains the import path. For all other
/// imports, it contains the URL passed to the `@import`.
final Set<String> includedFiles;
/// The canonical URLs of all stylesheets loaded during compilation.
final Set<Uri> includedUrls;
EvaluateResult(this.stylesheet, this.includedFiles);
EvaluateResult(this.stylesheet, this.includedUrls);
}
/// The result of evaluating arguments to a function or mixin.

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: b2321a00031707d2df699e6888a334deba39995d
// Checksum: 5e03597bf64a5e03b483e64e4d35e4e86288bc10
//
// ignore_for_file: unused_import
@ -224,12 +224,8 @@ class _EvaluateVisitor
/// Whether we're currently building the output of a `@keyframes` rule.
var _inKeyframes = false;
/// The set that will eventually populate the JS API's
/// `result.stats.includedFiles` field.
///
/// For filesystem imports, this contains the import path. For all other
/// imports, it contains the URL passed to the `@import`.
final _includedFiles = <String>{};
/// The canonical URLs of all stylesheets loaded during compilation.
final _includedUrls = <Uri>{};
/// A map from canonical URLs for modules (or imported files) that are
/// currently being evaluated to AST nodes whose spans indicate the original
@ -513,18 +509,12 @@ class _EvaluateVisitor
var url = node.span.sourceUrl;
if (url != null) {
_activeModules[url] = null;
if (_asNodeSass) {
if (url.scheme == 'file') {
_includedFiles.add(p.fromUri(url));
} else if (url.toString() != 'stdin') {
_includedFiles.add(url.toString());
}
}
if (!(_asNodeSass && url.toString() == 'stdin')) _includedUrls.add(url);
}
var module = _execute(importer, node);
return EvaluateResult(_combineCss(module), _includedFiles);
return EvaluateResult(_combineCss(module), _includedUrls);
});
}
@ -1575,13 +1565,17 @@ class _EvaluateVisitor
var stylesheet = importCache.importCanonical(tuple.item1, tuple.item2,
originalUrl: tuple.item3, quiet: _quietDeps && isDependency);
if (stylesheet != null) {
_includedUrls.add(tuple.item2);
return _LoadedStylesheet(stylesheet,
importer: tuple.item1, isDependency: isDependency);
}
}
} else {
var result = _importLikeNode(url, forImport);
if (result != null) return result;
if (result != null) {
result.stylesheet.span.sourceUrl.andThen(_includedUrls.add);
return result;
}
}
if (url.startsWith('package:') && isNode) {
@ -1626,8 +1620,6 @@ class _EvaluateVisitor
var contents = result.item1;
var url = result.item2;
_includedFiles.add(url.startsWith('file:') ? p.fromUri(url) : url);
return _LoadedStylesheet(
Stylesheet.parse(contents,
url.startsWith('file') ? Syntax.forPath(url) : Syntax.scss,

View File

@ -1,5 +1,5 @@
name: sass
version: 1.35.2
version: 1.36.0
description: A Sass implementation in Dart.
author: Sass Team
homepage: https://github.com/sass/dart-sass

View File

@ -5,7 +5,6 @@
import 'dart:convert';
import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
import 'package:test_process/test_process.dart';
@ -35,9 +34,9 @@ void sharedTests(Future<TestProcess> runSass(Iterable<String> arguments)) {
});
test("contains mappings", () {
late SingleMapping sourceMap;
sass.compileString("a {b: 1 + 2}", sourceMap: (map) => sourceMap = map);
expect(map, containsPair("mappings", sourceMap.toJson()["mappings"]));
var result = sass.compileStringToResult("a {b: 1 + 2}");
expect(map,
containsPair("mappings", result.sourceMap!.toJson()["mappings"]));
});
});
@ -288,9 +287,9 @@ void sharedTests(Future<TestProcess> runSass(Iterable<String> arguments)) {
});
test("contains mappings in the generated CSS", () {
late SingleMapping sourceMap;
sass.compileString("a {b: 1 + 2}", sourceMap: (map) => sourceMap = map);
expect(map, containsPair("mappings", sourceMap.toJson()["mappings"]));
var result = sass.compileStringToResult("a {b: 1 + 2}");
expect(map,
containsPair("mappings", result.sourceMap!.toJson()["mappings"]));
});
test("refers to the source file", () {

View File

@ -6,7 +6,6 @@
import 'dart:convert';
import 'package:source_maps/source_maps.dart';
import 'package:test/test.dart';
import 'package:sass/sass.dart';
@ -115,33 +114,27 @@ void main() {
});
test("uses an importer's source map URL", () {
late SingleMapping map;
compileString('@import "orange";',
importers: [
TestImporter((url) => Uri.parse("u:$url"), (url) {
var color = url.path;
return ImporterResult('.$color {color: $color}',
sourceMapUrl: Uri.parse("u:blue"), indented: false);
})
],
sourceMap: (map_) => map = map_);
var result = compileStringToResult('@import "orange";', importers: [
TestImporter((url) => Uri.parse("u:$url"), (url) {
var color = url.path;
return ImporterResult('.$color {color: $color}',
sourceMapUrl: Uri.parse("u:blue"), indented: false);
})
]);
expect(map.urls, contains("u:blue"));
expect(result.sourceMap!.urls, contains("u:blue"));
});
test("uses a data: source map URL if the importer doesn't provide one", () {
late SingleMapping map;
compileString('@import "orange";',
importers: [
TestImporter((url) => Uri.parse("u:$url"), (url) {
var color = url.path;
return ImporterResult('.$color {color: $color}', indented: false);
})
],
sourceMap: (map_) => map = map_);
var result = compileStringToResult('@import "orange";', importers: [
TestImporter((url) => Uri.parse("u:$url"), (url) {
var color = url.path;
return ImporterResult('.$color {color: $color}', indented: false);
})
]);
expect(
map.urls,
result.sourceMap!.urls,
contains(Uri.dataFromString(".orange {color: orange}", encoding: utf8)
.toString()));
});

View File

@ -226,6 +226,80 @@ a {
});
});
group("includedUrls", () {
group("contains the entrypoint's URL", () {
group("in compileStringToResult()", () {
test("if it's given", () {
var result = compileStringToResult("a {b: c}", url: "source.scss");
expect(result.includedUrls, equals([Uri.parse("source.scss")]));
});
test("unless it's not given", () {
var result = compileStringToResult("a {b: c}");
expect(result.includedUrls, isEmpty);
});
});
test("in compileToResult()", () async {
await d.file("input.scss", "a {b: c};").create();
var result = compileToResult(d.path('input.scss'));
expect(result.includedUrls, equals([p.toUri(d.path('input.scss'))]));
});
});
test("contains a URL loaded via @import", () async {
await d.file("_other.scss", "a {b: c}").create();
await d.file("input.scss", "@import 'other';").create();
var result = compileToResult(d.path('input.scss'));
expect(result.includedUrls, contains(p.toUri(d.path('_other.scss'))));
});
test("contains a URL loaded via @use", () async {
await d.file("_other.scss", "a {b: c}").create();
await d.file("input.scss", "@use 'other';").create();
var result = compileToResult(d.path('input.scss'));
expect(result.includedUrls, contains(p.toUri(d.path('_other.scss'))));
});
test("contains a URL loaded via @forward", () async {
await d.file("_other.scss", "a {b: c}").create();
await d.file("input.scss", "@forward 'other';").create();
var result = compileToResult(d.path('input.scss'));
expect(result.includedUrls, contains(p.toUri(d.path('_other.scss'))));
});
test("contains a URL loaded via @meta.load-css()", () async {
await d.file("_other.scss", "a {b: c}").create();
await d.file("input.scss", """
@use 'sass:meta';
@include meta.load-css('other');
""").create();
var result = compileToResult(d.path('input.scss'));
expect(result.includedUrls, contains(p.toUri(d.path('_other.scss'))));
});
test("contains a URL loaded via a chain of loads", () async {
await d.file("_jupiter.scss", "a {b: c}").create();
await d.file("_mars.scss", "@forward 'jupiter';").create();
await d.file("_earth.scss", "@import 'mars';").create();
await d.file("_venus.scss", "@use 'earth';").create();
await d.file("mercury.scss", """
@use 'sass:meta';
@include meta.load-css('venus');
""").create();
var result = compileToResult(d.path('mercury.scss'));
expect(
result.includedUrls,
unorderedEquals([
p.toUri(d.path('mercury.scss')),
p.toUri(d.path('_venus.scss')),
p.toUri(d.path('_earth.scss')),
p.toUri(d.path('_mars.scss')),
p.toUri(d.path('_jupiter.scss'))
]));
});
});
// Regression test for #1318
test("meta.load-module() doesn't have a race condition", () async {
await d.file("other.scss", '/**//**/').create();

View File

@ -27,7 +27,7 @@ final sources = const {
/// Classes that are defined in the async version of a file and used as-is in
/// the sync version, and thus should not be copied over.
final _sharedClasses = const ['EvaluateResult', 'CompileResult'];
final _sharedClasses = const ['EvaluateResult'];
/// This is how we support both synchronous and asynchronous compilation modes.
///
@ -97,7 +97,6 @@ class _Visitor extends RecursiveAstVisitor<void> {
_buffer.writeln();
} else if (p.basename(path) == 'async_compile.dart') {
_buffer.writeln();
_buffer.writeln("import 'async_compile.dart';");
_buffer.writeln("export 'async_compile.dart';");
_buffer.writeln();
}