mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-22 22:02:00 +01:00
Implement FileImporter (#57)
This commit is contained in:
parent
17f1e69a46
commit
1cbb0c5417
@ -1,3 +1,7 @@
|
||||
## 1.0.0-beta.14
|
||||
|
||||
* Support `FileImporter`s.
|
||||
|
||||
## 1.0.0-beta.13
|
||||
|
||||
* Report a better error message for an empty `CompileRequest.Input.path`.
|
||||
|
@ -12,7 +12,8 @@ import 'package:sass_embedded/src/dispatcher.dart';
|
||||
import 'package:sass_embedded/src/embedded_sass.pb.dart';
|
||||
import 'package:sass_embedded/src/function_registry.dart';
|
||||
import 'package:sass_embedded/src/host_callable.dart';
|
||||
import 'package:sass_embedded/src/importer.dart';
|
||||
import 'package:sass_embedded/src/importer/file.dart';
|
||||
import 'package:sass_embedded/src/importer/host.dart';
|
||||
import 'package:sass_embedded/src/logger.dart';
|
||||
import 'package:sass_embedded/src/util/length_delimited_transformer.dart';
|
||||
import 'package:sass_embedded/src/utils.dart';
|
||||
@ -125,7 +126,7 @@ void main(List<String> args) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Converts [importer] into an [Importer].
|
||||
/// Converts [importer] into a [sass.Importer].
|
||||
sass.Importer? _decodeImporter(
|
||||
Dispatcher dispatcher,
|
||||
InboundMessage_CompileRequest request,
|
||||
@ -135,10 +136,10 @@ sass.Importer? _decodeImporter(
|
||||
return sass.FilesystemImporter(importer.path);
|
||||
|
||||
case InboundMessage_CompileRequest_Importer_Importer.importerId:
|
||||
return Importer(dispatcher, request.id, importer.importerId);
|
||||
return HostImporter(dispatcher, request.id, importer.importerId);
|
||||
|
||||
case InboundMessage_CompileRequest_Importer_Importer.fileImporterId:
|
||||
throw "CompileRequest.Importer.fileImporterId is not yet supported";
|
||||
return FileImporter(dispatcher, request.id, importer.fileImporterId);
|
||||
|
||||
case InboundMessage_CompileRequest_Importer_Importer.notSet:
|
||||
return null;
|
||||
|
@ -80,6 +80,11 @@ class Dispatcher {
|
||||
_dispatchResponse(response.id, response);
|
||||
break;
|
||||
|
||||
case InboundMessage_Message.fileImportResponse:
|
||||
var response = message.fileImportResponse;
|
||||
_dispatchResponse(response.id, response);
|
||||
break;
|
||||
|
||||
case InboundMessage_Message.functionCallResponse:
|
||||
var response = message.functionCallResponse;
|
||||
_dispatchResponse(response.id, response);
|
||||
@ -131,6 +136,11 @@ class Dispatcher {
|
||||
_sendRequest<InboundMessage_ImportResponse>(
|
||||
OutboundMessage()..importRequest = request);
|
||||
|
||||
Future<InboundMessage_FileImportResponse> sendFileImportRequest(
|
||||
OutboundMessage_FileImportRequest request) =>
|
||||
_sendRequest<InboundMessage_FileImportResponse>(
|
||||
OutboundMessage()..fileImportRequest = request);
|
||||
|
||||
Future<InboundMessage_FunctionCallResponse> sendFunctionCallRequest(
|
||||
OutboundMessage_FunctionCallRequest request) =>
|
||||
_sendRequest<InboundMessage_FunctionCallResponse>(
|
||||
@ -165,8 +175,12 @@ class Dispatcher {
|
||||
/// Throws an error if there's no outstanding request with the given [id] or
|
||||
/// if that request is expecting a different type of response.
|
||||
void _dispatchResponse<T extends GeneratedMessage>(int id, T response) {
|
||||
var completer =
|
||||
id < _outstandingRequests.length ? _outstandingRequests[id] : null;
|
||||
Completer<GeneratedMessage>? completer;
|
||||
if (id < _outstandingRequests.length) {
|
||||
completer = _outstandingRequests[id];
|
||||
_outstandingRequests[id] = null;
|
||||
}
|
||||
|
||||
if (completer == null) {
|
||||
throw paramsError(
|
||||
"Response ID $id doesn't match any outstanding requests.");
|
||||
|
45
lib/src/importer/base.dart
Normal file
45
lib/src/importer/base.dart
Normal file
@ -0,0 +1,45 @@
|
||||
// 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:sass_api/sass_api.dart' as sass;
|
||||
|
||||
import '../dispatcher.dart';
|
||||
import '../embedded_sass.pb.dart' hide SourceSpan;
|
||||
import '../utils.dart';
|
||||
|
||||
/// An abstract base class for importers that communicate with the host in some
|
||||
/// way.
|
||||
abstract class ImporterBase extends sass.Importer {
|
||||
/// The [Dispatcher] to which to send requests.
|
||||
@protected
|
||||
final Dispatcher dispatcher;
|
||||
|
||||
ImporterBase(this.dispatcher);
|
||||
|
||||
/// Parses [url] as a [Uri] and throws an error if it's invalid or relative
|
||||
/// (including root-relative).
|
||||
///
|
||||
/// The [field] name is used in the error message if one is thrown.
|
||||
@protected
|
||||
Uri parseAbsoluteUrl(String field, String url) {
|
||||
Uri parsedUrl;
|
||||
try {
|
||||
parsedUrl = Uri.parse(url);
|
||||
} on FormatException catch (error) {
|
||||
sendAndThrow(paramsError("$field is invalid: $error"));
|
||||
}
|
||||
|
||||
if (parsedUrl.scheme.isNotEmpty) return parsedUrl;
|
||||
sendAndThrow(paramsError('$field must be absolute, was "$parsedUrl"'));
|
||||
}
|
||||
|
||||
/// Sends [error] to the remote endpoint, and also throws it so that the Sass
|
||||
/// compilation fails.
|
||||
@protected
|
||||
Never sendAndThrow(ProtocolError error) {
|
||||
dispatcher.sendError(error);
|
||||
throw "Protocol error: ${error.message}";
|
||||
}
|
||||
}
|
66
lib/src/importer/file.dart
Normal file
66
lib/src/importer/file.dart
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 'dart:cli';
|
||||
|
||||
import 'package:sass_api/sass_api.dart' as sass;
|
||||
|
||||
import '../dispatcher.dart';
|
||||
import '../embedded_sass.pb.dart' hide SourceSpan;
|
||||
import '../utils.dart';
|
||||
import 'base.dart';
|
||||
|
||||
/// A filesystem importer to use for most implementation details of
|
||||
/// [FileImporter].
|
||||
///
|
||||
/// This allows us to avoid duplicating logic between the two importers.
|
||||
final _filesystemImporter = sass.FilesystemImporter('.');
|
||||
|
||||
/// An importer that asks the host to resolve imports in a simplified,
|
||||
/// file-system-centric way.
|
||||
class FileImporter extends ImporterBase {
|
||||
/// The ID of the compilation in which this importer is used.
|
||||
final int _compilationId;
|
||||
|
||||
/// The host-provided ID of the importer to invoke.
|
||||
final int _importerId;
|
||||
|
||||
FileImporter(Dispatcher dispatcher, this._compilationId, this._importerId)
|
||||
: super(dispatcher);
|
||||
|
||||
Uri? canonicalize(Uri url) {
|
||||
if (url.scheme == 'file') return _filesystemImporter.canonicalize(url);
|
||||
|
||||
return waitFor(() async {
|
||||
var response = await dispatcher
|
||||
.sendFileImportRequest(OutboundMessage_FileImportRequest()
|
||||
..compilationId = _compilationId
|
||||
..importerId = _importerId
|
||||
..url = url.toString()
|
||||
..fromImport = fromImport);
|
||||
|
||||
switch (response.whichResult()) {
|
||||
case InboundMessage_FileImportResponse_Result.fileUrl:
|
||||
var url =
|
||||
parseAbsoluteUrl("FileImportResponse.file_url", response.fileUrl);
|
||||
if (url.scheme != 'file') {
|
||||
sendAndThrow(paramsError(
|
||||
'FileImportResponse.file_url must be a file: URL, was "$url"'));
|
||||
}
|
||||
|
||||
return _filesystemImporter.canonicalize(url);
|
||||
|
||||
case InboundMessage_FileImportResponse_Result.error:
|
||||
throw response.error;
|
||||
|
||||
case InboundMessage_FileImportResponse_Result.notSet:
|
||||
return null;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
sass.ImporterResult? load(Uri url) => _filesystemImporter.load(url);
|
||||
|
||||
String toString() => "FileImporter";
|
||||
}
|
@ -6,26 +6,25 @@ import 'dart:cli';
|
||||
|
||||
import 'package:sass_api/sass_api.dart' as sass;
|
||||
|
||||
import 'dispatcher.dart';
|
||||
import 'embedded_sass.pb.dart' hide SourceSpan;
|
||||
import 'utils.dart';
|
||||
import '../dispatcher.dart';
|
||||
import '../embedded_sass.pb.dart' hide SourceSpan;
|
||||
import '../utils.dart';
|
||||
import 'base.dart';
|
||||
|
||||
/// An importer that asks the host to resolve imports.
|
||||
class Importer extends sass.Importer {
|
||||
/// The [Dispatcher] to which to send requests.
|
||||
final Dispatcher _dispatcher;
|
||||
|
||||
class HostImporter extends ImporterBase {
|
||||
/// The ID of the compilation in which this importer is used.
|
||||
final int _compilationId;
|
||||
|
||||
/// The host-provided ID of the importer to invoke.
|
||||
final int _importerId;
|
||||
|
||||
Importer(this._dispatcher, this._compilationId, this._importerId);
|
||||
HostImporter(Dispatcher dispatcher, this._compilationId, this._importerId)
|
||||
: super(dispatcher);
|
||||
|
||||
Uri? canonicalize(Uri url) {
|
||||
return waitFor(() async {
|
||||
var response = await _dispatcher
|
||||
var response = await dispatcher
|
||||
.sendCanonicalizeRequest(OutboundMessage_CanonicalizeRequest()
|
||||
..compilationId = _compilationId
|
||||
..importerId = _importerId
|
||||
@ -34,7 +33,7 @@ class Importer extends sass.Importer {
|
||||
|
||||
switch (response.whichResult()) {
|
||||
case InboundMessage_CanonicalizeResponse_Result.url:
|
||||
return _parseAbsoluteUrl("CanonicalizeResponse.url", response.url);
|
||||
return parseAbsoluteUrl("CanonicalizeResponse.url", response.url);
|
||||
|
||||
case InboundMessage_CanonicalizeResponse_Result.error:
|
||||
throw response.error;
|
||||
@ -48,7 +47,7 @@ class Importer extends sass.Importer {
|
||||
sass.ImporterResult load(Uri url) {
|
||||
return waitFor(() async {
|
||||
var response =
|
||||
await _dispatcher.sendImportRequest(OutboundMessage_ImportRequest()
|
||||
await dispatcher.sendImportRequest(OutboundMessage_ImportRequest()
|
||||
..compilationId = _compilationId
|
||||
..importerId = _importerId
|
||||
..url = url.toString());
|
||||
@ -58,7 +57,7 @@ class Importer extends sass.Importer {
|
||||
return sass.ImporterResult(response.success.contents,
|
||||
sourceMapUrl: response.success.sourceMapUrl.isEmpty
|
||||
? null
|
||||
: _parseAbsoluteUrl("ImportResponse.success.source_map_url",
|
||||
: parseAbsoluteUrl("ImportResponse.success.source_map_url",
|
||||
response.success.sourceMapUrl),
|
||||
syntax: syntaxToSyntax(response.success.syntax));
|
||||
|
||||
@ -66,33 +65,10 @@ class Importer extends sass.Importer {
|
||||
throw response.error;
|
||||
|
||||
case InboundMessage_ImportResponse_Result.notSet:
|
||||
_sendAndThrow(mandatoryError("ImportResponse.result"));
|
||||
sendAndThrow(mandatoryError("ImportResponse.result"));
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
/// Parses [url] as a [Uri] and throws an error if it's invalid or relative
|
||||
/// (including root-relative).
|
||||
///
|
||||
/// The [field] name is used in the error message if one is thrown.
|
||||
Uri _parseAbsoluteUrl(String field, String url) {
|
||||
Uri parsedUrl;
|
||||
try {
|
||||
parsedUrl = Uri.parse(url);
|
||||
} on FormatException catch (error) {
|
||||
_sendAndThrow(paramsError("$field is invalid: $error"));
|
||||
}
|
||||
|
||||
if (parsedUrl.scheme.isNotEmpty) return parsedUrl;
|
||||
_sendAndThrow(paramsError('$field must be absolute, was "$parsedUrl"'));
|
||||
}
|
||||
|
||||
/// Sends [error] to the remote endpoint, and also throws it so that the Sass
|
||||
/// compilation fails.
|
||||
Never _sendAndThrow(ProtocolError error) {
|
||||
_dispatcher.sendError(error);
|
||||
throw "Protocol error: ${error.message}";
|
||||
}
|
||||
|
||||
String toString() => "HostImporter";
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
name: sass_embedded
|
||||
version: 1.0.0-beta.13
|
||||
version: 1.0.0-beta.14
|
||||
description: An implementation of the Sass embedded protocol using Dart Sass.
|
||||
author: Sass Team
|
||||
homepage: https://github.com/sass/dart-sass-embedded
|
||||
|
||||
environment:
|
||||
|
280
test/file_importer_test.dart
Normal file
280
test/file_importer_test.dart
Normal file
@ -0,0 +1,280 @@
|
||||
// Copyright 2021 Google LLC. 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:path/path.dart' as p;
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_descriptor/test_descriptor.dart' as d;
|
||||
|
||||
import 'package:sass_embedded/src/embedded_sass.pb.dart';
|
||||
import 'package:sass_embedded/src/utils.dart';
|
||||
|
||||
import 'embedded_process.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
late EmbeddedProcess process;
|
||||
setUp(() async {
|
||||
process = await EmbeddedProcess.start();
|
||||
});
|
||||
|
||||
group("emits a protocol error", () {
|
||||
late OutboundMessage_FileImportRequest request;
|
||||
|
||||
setUp(() async {
|
||||
process.inbound.add(compileString("@import 'other'", importers: [
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = 1
|
||||
]));
|
||||
|
||||
request = getFileImportRequest(await process.outbound.next);
|
||||
});
|
||||
|
||||
test("for a response without a corresponding request ID", () async {
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse =
|
||||
(InboundMessage_FileImportResponse()..id = request.id + 1));
|
||||
|
||||
await expectParamsError(
|
||||
process,
|
||||
errorId,
|
||||
"Response ID ${request.id + 1} doesn't match any outstanding "
|
||||
"requests.");
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("for a response that doesn't match the request type", () async {
|
||||
process.inbound.add(InboundMessage()
|
||||
..canonicalizeResponse =
|
||||
(InboundMessage_CanonicalizeResponse()..id = request.id));
|
||||
|
||||
await expectParamsError(
|
||||
process,
|
||||
errorId,
|
||||
"Request ID ${request.id} doesn't match response type "
|
||||
"InboundMessage_CanonicalizeResponse.");
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
group("for a FileImportResponse with a URL", () {
|
||||
test("that's empty", () async {
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..fileUrl = ""));
|
||||
|
||||
await _expectImportParamsError(
|
||||
process, 'FileImportResponse.file_url must be absolute, was ""');
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("that's relative", () async {
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..fileUrl = "foo"));
|
||||
|
||||
await _expectImportParamsError(
|
||||
process, 'FileImportResponse.file_url must be absolute, was "foo"');
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("that's not file:", () async {
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..fileUrl = "other:foo"));
|
||||
|
||||
await _expectImportParamsError(process,
|
||||
'FileImportResponse.file_url must be a file: URL, was "other:foo"');
|
||||
await process.kill();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("includes in FileImportRequest", () {
|
||||
var compilationId = 1234;
|
||||
var importerId = 5679;
|
||||
late OutboundMessage_FileImportRequest request;
|
||||
setUp(() async {
|
||||
process.inbound.add(
|
||||
compileString("@import 'other'", id: compilationId, importers: [
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = importerId
|
||||
]));
|
||||
request = getFileImportRequest(await process.outbound.next);
|
||||
});
|
||||
|
||||
test("the same compilationId as the compilation", () async {
|
||||
expect(request.compilationId, equals(compilationId));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("a known importerId", () async {
|
||||
expect(request.importerId, equals(importerId));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("the imported URL", () async {
|
||||
expect(request.url, equals("other"));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("whether the import came from an @import", () async {
|
||||
expect(request.fromImport, isTrue);
|
||||
await process.kill();
|
||||
});
|
||||
});
|
||||
|
||||
test("errors cause compilation to fail", () async {
|
||||
process.inbound.add(compileString("@import 'other'", importers: [
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = 1
|
||||
]));
|
||||
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..error = "oh no"));
|
||||
|
||||
var failure = getCompileFailure(await process.outbound.next);
|
||||
expect(failure.message, equals('oh no'));
|
||||
expect(failure.span.text, equals("'other'"));
|
||||
expect(failure.stackTrace, equals('- 1:9 root stylesheet\n'));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("null results count as not found", () async {
|
||||
process.inbound.add(compileString("@import 'other'", importers: [
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = 1
|
||||
]));
|
||||
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse =
|
||||
(InboundMessage_FileImportResponse()..id = request.id));
|
||||
|
||||
var failure = getCompileFailure(await process.outbound.next);
|
||||
expect(failure.message, equals("Can't find stylesheet to import."));
|
||||
expect(failure.span.text, equals("'other'"));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
group("attempts importers in order", () {
|
||||
test("with multiple file importers", () async {
|
||||
process.inbound.add(compileString("@import 'other'", importers: [
|
||||
for (var i = 0; i < 10; i++)
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = i
|
||||
]));
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
expect(request.importerId, equals(i));
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse =
|
||||
(InboundMessage_FileImportResponse()..id = request.id));
|
||||
}
|
||||
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("with a mixture of file and normal importers", () async {
|
||||
process.inbound.add(compileString("@import 'other'", importers: [
|
||||
for (var i = 0; i < 10; i++)
|
||||
if (i % 2 == 0)
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = i
|
||||
else
|
||||
InboundMessage_CompileRequest_Importer()..importerId = i
|
||||
]));
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (i % 2 == 0) {
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
expect(request.importerId, equals(i));
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse =
|
||||
(InboundMessage_FileImportResponse()..id = request.id));
|
||||
} else {
|
||||
var request = getCanonicalizeRequest(await process.outbound.next);
|
||||
expect(request.importerId, equals(i));
|
||||
process.inbound.add(InboundMessage()
|
||||
..canonicalizeResponse =
|
||||
(InboundMessage_CanonicalizeResponse()..id = request.id));
|
||||
}
|
||||
}
|
||||
|
||||
await process.kill();
|
||||
});
|
||||
});
|
||||
|
||||
test("tries resolved URL as a relative path first", () async {
|
||||
await d.file("upstream.scss", "a {b: c}").create();
|
||||
await d.file("midstream.scss", "@import 'upstream';").create();
|
||||
|
||||
process.inbound.add(compileString("@import 'midstream'", importers: [
|
||||
for (var i = 0; i < 10; i++)
|
||||
InboundMessage_CompileRequest_Importer()..fileImporterId = i
|
||||
]));
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
expect(request.url, equals("midstream"));
|
||||
expect(request.importerId, equals(i));
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse =
|
||||
(InboundMessage_FileImportResponse()..id = request.id));
|
||||
}
|
||||
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
expect(request.importerId, equals(5));
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..fileUrl = p.toUri(d.path("midstream")).toString()));
|
||||
|
||||
await expectLater(process.outbound, emits(isSuccess("a { b: c; }")));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
group("handles an importer for a string compile request", () {
|
||||
setUp(() async {
|
||||
await d.file("other.scss", "a {b: c}").create();
|
||||
});
|
||||
|
||||
test("without a base URL", () async {
|
||||
process.inbound.add(compileString("@import 'other'",
|
||||
importer: InboundMessage_CompileRequest_Importer()
|
||||
..fileImporterId = 1));
|
||||
|
||||
var request = getFileImportRequest(await process.outbound.next);
|
||||
expect(request.url, equals("other"));
|
||||
|
||||
process.inbound.add(InboundMessage()
|
||||
..fileImportResponse = (InboundMessage_FileImportResponse()
|
||||
..id = request.id
|
||||
..fileUrl = p.toUri(d.path("other")).toString()));
|
||||
|
||||
await expectLater(process.outbound, emits(isSuccess("a { b: c; }")));
|
||||
await process.kill();
|
||||
});
|
||||
|
||||
test("with a base URL", () async {
|
||||
process.inbound.add(compileString("@import 'other'",
|
||||
url: p.toUri(d.path("input")).toString(),
|
||||
importer: InboundMessage_CompileRequest_Importer()
|
||||
..fileImporterId = 1));
|
||||
|
||||
await expectLater(process.outbound, emits(isSuccess("a { b: c; }")));
|
||||
await process.kill();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Asserts that [process] emits a [ProtocolError] params error with the given
|
||||
/// [message] on its protobuf stream and causes the compilation to fail.
|
||||
Future<void> _expectImportParamsError(EmbeddedProcess process, message) async {
|
||||
await expectLater(process.outbound,
|
||||
emits(isProtocolError(errorId, ProtocolErrorType.PARAMS, message)));
|
||||
|
||||
var failure = getCompileFailure(await process.outbound.next);
|
||||
expect(failure.message, equals('Protocol error: $message'));
|
||||
expect(failure.span.text, equals("'other'"));
|
||||
}
|
@ -103,6 +103,16 @@ OutboundMessage_ImportRequest getImportRequest(value) {
|
||||
return message.importRequest;
|
||||
}
|
||||
|
||||
/// Asserts that [message] is an [OutboundMessage] with a `FileImportRequest`
|
||||
/// and returns it.
|
||||
OutboundMessage_FileImportRequest getFileImportRequest(value) {
|
||||
expect(value, isA<OutboundMessage>());
|
||||
var message = value as OutboundMessage;
|
||||
expect(message.hasFileImportRequest(), isTrue,
|
||||
reason: "Expected $message to have a FileImportRequest");
|
||||
return message.fileImportRequest;
|
||||
}
|
||||
|
||||
/// Asserts that [message] is an [OutboundMessage] with a
|
||||
/// `FunctionCallRequest` and returns it.
|
||||
OutboundMessage_FunctionCallRequest getFunctionCallRequest(value) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user