mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-23 06:12:00 +01:00
Add this.fromImport
for JS importers
See sass/sass#3055 See webpack-contrib/sass-loader#905
This commit is contained in:
parent
5d4950db36
commit
8a344e343f
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
||||
## 1.33.0
|
||||
|
||||
### JS API
|
||||
|
||||
* The `this` context for importers now has a `fromImport` field, which is `true`
|
||||
if the importer is being invoked from an `@import` and `false` otherwise.
|
||||
Importers should only use this to determine whether to load [import-only
|
||||
files].
|
||||
|
||||
[import-only files]: https://sass-lang.com/documentation/at-rules/import#import-only-files
|
||||
|
||||
## 1.32.13
|
||||
|
||||
* **Potentially breaking bug fix:** Null values in `@use` and `@forward`
|
||||
|
@ -13,6 +13,7 @@ import '../../node/function.dart';
|
||||
import '../../node/importer_result.dart';
|
||||
import '../../node/utils.dart';
|
||||
import '../../util/nullable.dart';
|
||||
import '../../node/render_context.dart';
|
||||
import '../utils.dart';
|
||||
|
||||
/// An importer that encapsulates Node Sass's import logic.
|
||||
@ -40,8 +41,12 @@ import '../utils.dart';
|
||||
/// 4. Filesystem imports relative to an `includePaths` path.
|
||||
/// 5. Filesystem imports relative to a `SASS_PATH` path.
|
||||
class NodeImporter {
|
||||
/// The `this` context in which importer functions are invoked.
|
||||
final Object _context;
|
||||
/// The options for the `this` context in which importer functions are
|
||||
/// invoked.
|
||||
///
|
||||
/// This is typed as [Object] because the public interface of [NodeImporter]
|
||||
/// is shared with the VM, which can't handle JS interop types.
|
||||
final Object _options;
|
||||
|
||||
/// The include paths passed in by the user.
|
||||
final List<String> _includePaths;
|
||||
@ -50,7 +55,7 @@ class NodeImporter {
|
||||
final List<JSFunction> _importers;
|
||||
|
||||
NodeImporter(
|
||||
this._context, Iterable<String> includePaths, Iterable<Object> importers)
|
||||
this._options, Iterable<String> includePaths, Iterable<Object> importers)
|
||||
: _includePaths = List.unmodifiable(_addSassPath(includePaths)),
|
||||
_importers = List.unmodifiable(importers.cast());
|
||||
|
||||
@ -78,7 +83,8 @@ class NodeImporter {
|
||||
// The previous URL is always an absolute file path for filesystem imports.
|
||||
var previousString = _previousToString(previous);
|
||||
for (var importer in _importers) {
|
||||
var value = call2(importer, _context, url, previousString);
|
||||
var value =
|
||||
call2(importer, _renderContext(forImport), url, previousString);
|
||||
if (value != null) {
|
||||
return _handleImportResult(url, previous, value, forImport);
|
||||
}
|
||||
@ -103,7 +109,8 @@ class NodeImporter {
|
||||
// The previous URL is always an absolute file path for filesystem imports.
|
||||
var previousString = _previousToString(previous);
|
||||
for (var importer in _importers) {
|
||||
var value = await _callImporterAsync(importer, url, previousString);
|
||||
var value =
|
||||
await _callImporterAsync(importer, url, previousString, forImport);
|
||||
if (value != null) {
|
||||
return _handleImportResult(url, previous, value, forImport);
|
||||
}
|
||||
@ -193,13 +200,17 @@ class NodeImporter {
|
||||
}
|
||||
|
||||
/// Calls an importer that may or may not be asynchronous.
|
||||
Future<Object?> _callImporterAsync(
|
||||
JSFunction importer, String url, String previousString) async {
|
||||
Future<Object?> _callImporterAsync(JSFunction importer, String url,
|
||||
String previousString, bool forImport) async {
|
||||
var completer = Completer<Object>();
|
||||
|
||||
var result = call3(importer, _context, url, previousString,
|
||||
var result = call3(importer, _renderContext(forImport), url, previousString,
|
||||
allowInterop(completer.complete));
|
||||
if (isUndefined(result)) return await completer.future;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns the [RenderContext] in which to invoke importers.
|
||||
RenderContext _renderContext(bool fromImport) => RenderContext(
|
||||
options: _options as RenderContextOptions, fromImport: fromImport);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class NodeImporter {
|
||||
NodeImporter(Object context, Iterable<String> includePaths,
|
||||
NodeImporter(Object options, Iterable<String> includePaths,
|
||||
Iterable<Object> importers);
|
||||
|
||||
Tuple2<String, String>? load(String url, Uri? previous, bool forImport) =>
|
||||
|
@ -204,7 +204,7 @@ List<AsyncCallable> _parseFunctions(RenderOptions options, DateTime start,
|
||||
'Invalid signature "$signature": ${error.message}', error.span);
|
||||
}
|
||||
|
||||
var context = _contextWithOptions(options, start);
|
||||
var context = RenderContext(options: _contextOptions(options, start));
|
||||
|
||||
var fiber = options.fiber;
|
||||
if (fiber != null) {
|
||||
@ -261,9 +261,8 @@ NodeImporter _parseImporter(RenderOptions options, DateTime start) {
|
||||
importers = [options.importer as JSFunction];
|
||||
}
|
||||
|
||||
var context = importers.isNotEmpty
|
||||
? _contextWithOptions(options, start)
|
||||
: const Object();
|
||||
var contextOptions =
|
||||
importers.isNotEmpty ? _contextOptions(options, start) : Object();
|
||||
|
||||
var fiber = options.fiber;
|
||||
if (fiber != null) {
|
||||
@ -288,29 +287,26 @@ NodeImporter _parseImporter(RenderOptions options, DateTime start) {
|
||||
}
|
||||
|
||||
var includePaths = List<String>.from(options.includePaths ?? []);
|
||||
return NodeImporter(context, includePaths, importers);
|
||||
return NodeImporter(contextOptions, includePaths, importers);
|
||||
}
|
||||
|
||||
/// Creates a `this` context that contains the render options.
|
||||
RenderContext _contextWithOptions(RenderOptions options, DateTime start) {
|
||||
/// Creates the [RenderContextOptions] for the `this` context in which custom
|
||||
/// functions and importers will be evaluated.
|
||||
RenderContextOptions _contextOptions(RenderOptions options, DateTime start) {
|
||||
var includePaths = List<String>.from(options.includePaths ?? []);
|
||||
var context = RenderContext(
|
||||
options: RenderContextOptions(
|
||||
file: options.file,
|
||||
data: options.data,
|
||||
includePaths:
|
||||
([p.current, ...includePaths]).join(isWindows ? ';' : ':'),
|
||||
precision: SassNumber.precision,
|
||||
style: 1,
|
||||
indentType: options.indentType == 'tab' ? 1 : 0,
|
||||
indentWidth: _parseIndentWidth(options.indentWidth) ?? 2,
|
||||
linefeed: _parseLineFeed(options.linefeed).text,
|
||||
result: RenderContextResult(
|
||||
stats: RenderContextResultStats(
|
||||
start: start.millisecondsSinceEpoch,
|
||||
entry: options.file ?? 'data'))));
|
||||
context.options.context = context;
|
||||
return context;
|
||||
return RenderContextOptions(
|
||||
file: options.file,
|
||||
data: options.data,
|
||||
includePaths: ([p.current, ...includePaths]).join(isWindows ? ';' : ':'),
|
||||
precision: SassNumber.precision,
|
||||
style: 1,
|
||||
indentType: options.indentType == 'tab' ? 1 : 0,
|
||||
indentWidth: _parseIndentWidth(options.indentWidth) ?? 2,
|
||||
linefeed: _parseLineFeed(options.linefeed).text,
|
||||
result: RenderContextResult(
|
||||
stats: RenderContextResultStats(
|
||||
start: start.millisecondsSinceEpoch,
|
||||
entry: options.file ?? 'data')));
|
||||
}
|
||||
|
||||
/// Parse [style] into an [OutputStyle].
|
||||
|
@ -8,8 +8,10 @@ import 'package:js/js.dart';
|
||||
@anonymous
|
||||
class RenderContext {
|
||||
external RenderContextOptions get options;
|
||||
external bool? get fromImport;
|
||||
|
||||
external factory RenderContext({required RenderContextOptions options});
|
||||
external factory RenderContext(
|
||||
{required RenderContextOptions options, bool? fromImport});
|
||||
}
|
||||
|
||||
@JS()
|
||||
|
@ -414,7 +414,7 @@ class SassParser extends StylesheetParser {
|
||||
do {
|
||||
containsTab = false;
|
||||
containsSpace = false;
|
||||
nextIndentation = 0;
|
||||
nextIndentation = 0;
|
||||
|
||||
while (true) {
|
||||
var next = scanner.peekChar();
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: sass
|
||||
version: 1.32.13
|
||||
version: 1.33.0-dev
|
||||
description: A Sass implementation in Dart.
|
||||
author: Sass Team
|
||||
homepage: https://github.com/sass/dart-sass
|
||||
|
@ -556,6 +556,48 @@ void main() {
|
||||
}))));
|
||||
});
|
||||
});
|
||||
|
||||
group("includes a fromImport field that's", () {
|
||||
test("true for an @import", () {
|
||||
renderSync(RenderOptions(
|
||||
data: '@import "foo"',
|
||||
importer: allowInteropCaptureThis(
|
||||
expectAsync3((RenderContext this_, _, __) {
|
||||
expect(this_.fromImport, isTrue);
|
||||
return NodeImporterResult(contents: '');
|
||||
}))));
|
||||
});
|
||||
|
||||
test("false for a @use", () {
|
||||
renderSync(RenderOptions(
|
||||
data: '@use "foo"',
|
||||
importer: allowInteropCaptureThis(
|
||||
expectAsync3((RenderContext this_, _, __) {
|
||||
expect(this_.fromImport, isFalse);
|
||||
return NodeImporterResult(contents: '');
|
||||
}))));
|
||||
});
|
||||
|
||||
test("false for a @forward", () {
|
||||
renderSync(RenderOptions(
|
||||
data: '@forward "foo"',
|
||||
importer: allowInteropCaptureThis(
|
||||
expectAsync3((RenderContext this_, _, __) {
|
||||
expect(this_.fromImport, isFalse);
|
||||
return NodeImporterResult(contents: '');
|
||||
}))));
|
||||
});
|
||||
|
||||
test("false for meta.load-css()", () {
|
||||
renderSync(RenderOptions(
|
||||
data: '@use "sass:meta"; @include meta.load-css("foo")',
|
||||
importer: allowInteropCaptureThis(
|
||||
expectAsync3((RenderContext this_, _, __) {
|
||||
expect(this_.fromImport, isFalse);
|
||||
return NodeImporterResult(contents: '');
|
||||
}))));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("gracefully handles an error when", () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user