mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-27 12:44:42 +01:00
Add infrastructure for compiling multiple sources at once
This will allow us to use the same code path for --update as we do for normal compilation.
This commit is contained in:
parent
454603d160
commit
beff4a1011
@ -25,67 +25,9 @@ main(List<String> args) async {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
for (var source in options.sourcesToDestinations.keys) {
|
||||||
SingleMapping sourceMap;
|
var destination = options.sourcesToDestinations[source];
|
||||||
var sourceMapCallback =
|
await _compileStylesheet(options, source, destination);
|
||||||
options.emitSourceMap ? (SingleMapping map) => sourceMap = map : null;
|
|
||||||
|
|
||||||
var text =
|
|
||||||
options.readFromStdin ? await readStdin() : readFile(options.source);
|
|
||||||
var url = options.readFromStdin ? null : p.toUri(options.source);
|
|
||||||
var importer = new FilesystemImporter('.');
|
|
||||||
String css;
|
|
||||||
if (options.asynchronous) {
|
|
||||||
css = await compileStringAsync(text,
|
|
||||||
indented: options.indented,
|
|
||||||
logger: options.logger,
|
|
||||||
style: options.style,
|
|
||||||
importer: importer,
|
|
||||||
loadPaths: options.loadPaths,
|
|
||||||
url: url,
|
|
||||||
sourceMap: sourceMapCallback);
|
|
||||||
} else {
|
|
||||||
css = compileString(text,
|
|
||||||
indented: options.indented,
|
|
||||||
logger: options.logger,
|
|
||||||
style: options.style,
|
|
||||||
importer: importer,
|
|
||||||
loadPaths: options.loadPaths,
|
|
||||||
url: url,
|
|
||||||
sourceMap: sourceMapCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
css += _writeSourceMap(options, sourceMap);
|
|
||||||
if (options.writeToStdout) {
|
|
||||||
if (css.isNotEmpty) print(css);
|
|
||||||
} else {
|
|
||||||
ensureDir(p.dirname(options.destination));
|
|
||||||
writeFile(options.destination, css + "\n");
|
|
||||||
}
|
|
||||||
} on SassException catch (error, stackTrace) {
|
|
||||||
stderr.writeln(error.toString(color: options.color));
|
|
||||||
|
|
||||||
if (options.trace) {
|
|
||||||
stderr.writeln();
|
|
||||||
stderr.write(new Trace.from(stackTrace).terse.toString());
|
|
||||||
stderr.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit code 65 indicates invalid data per
|
|
||||||
// http://www.freebsd.org/cgi/man.cgi?query=sysexits.
|
|
||||||
exitCode = 65;
|
|
||||||
} on FileSystemException catch (error, stackTrace) {
|
|
||||||
stderr.writeln(
|
|
||||||
"Error reading ${p.relative(error.path)}: ${error.message}.");
|
|
||||||
|
|
||||||
// Error 66 indicates no input.
|
|
||||||
exitCode = 66;
|
|
||||||
|
|
||||||
if (options.trace) {
|
|
||||||
stderr.writeln();
|
|
||||||
stderr.write(new Trace.from(stackTrace).terse.toString());
|
|
||||||
stderr.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} on UsageException catch (error) {
|
} on UsageException catch (error) {
|
||||||
print("${error.message}\n");
|
print("${error.message}\n");
|
||||||
@ -125,14 +67,92 @@ Future<String> _loadVersion() async {
|
|||||||
.last;
|
.last;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the source map given by [mapping] to disk (if necessary) according to [options].
|
/// Compiles the stylesheet at [source] to [destination].
|
||||||
|
///
|
||||||
|
/// If [source] is `null`, that indicates that the stylesheet should be read
|
||||||
|
/// from stdin. If [destination] is `null`, that indicates that the stylesheet
|
||||||
|
/// should be emitted to stdout.
|
||||||
|
Future _compileStylesheet(
|
||||||
|
ExecutableOptions options, String source, String destination) async {
|
||||||
|
try {
|
||||||
|
SingleMapping sourceMap;
|
||||||
|
var sourceMapCallback =
|
||||||
|
options.emitSourceMap ? (SingleMapping map) => sourceMap = map : null;
|
||||||
|
|
||||||
|
var indented =
|
||||||
|
options.indented ?? (source != null && p.extension(source) == '.sass');
|
||||||
|
var text = source == null ? await readStdin() : readFile(source);
|
||||||
|
var url = source == null ? null : p.toUri(source);
|
||||||
|
var importer = new FilesystemImporter('.');
|
||||||
|
String css;
|
||||||
|
if (options.asynchronous) {
|
||||||
|
css = await compileStringAsync(text,
|
||||||
|
indented: indented,
|
||||||
|
logger: options.logger,
|
||||||
|
style: options.style,
|
||||||
|
importer: importer,
|
||||||
|
loadPaths: options.loadPaths,
|
||||||
|
url: url,
|
||||||
|
sourceMap: sourceMapCallback);
|
||||||
|
} else {
|
||||||
|
css = compileString(text,
|
||||||
|
indented: indented,
|
||||||
|
logger: options.logger,
|
||||||
|
style: options.style,
|
||||||
|
importer: importer,
|
||||||
|
loadPaths: options.loadPaths,
|
||||||
|
url: url,
|
||||||
|
sourceMap: sourceMapCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
css += _writeSourceMap(options, sourceMap, destination);
|
||||||
|
if (destination == null) {
|
||||||
|
if (css.isNotEmpty) print(css);
|
||||||
|
} else {
|
||||||
|
ensureDir(p.dirname(destination));
|
||||||
|
writeFile(destination, css + "\n");
|
||||||
|
}
|
||||||
|
} on SassException catch (error, stackTrace) {
|
||||||
|
stderr.writeln(error.toString(color: options.color));
|
||||||
|
|
||||||
|
if (options.trace) {
|
||||||
|
stderr.writeln();
|
||||||
|
stderr.write(new Trace.from(stackTrace).terse.toString());
|
||||||
|
stderr.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit code 65 indicates invalid data per
|
||||||
|
// http://www.freebsd.org/cgi/man.cgi?query=sysexits.
|
||||||
|
exitCode = 65;
|
||||||
|
} on FileSystemException catch (error, stackTrace) {
|
||||||
|
stderr
|
||||||
|
.writeln("Error reading ${p.relative(error.path)}: ${error.message}.");
|
||||||
|
|
||||||
|
// Error 66 indicates no input.
|
||||||
|
exitCode = 66;
|
||||||
|
|
||||||
|
if (options.trace) {
|
||||||
|
stderr.writeln();
|
||||||
|
stderr.write(new Trace.from(stackTrace).terse.toString());
|
||||||
|
stderr.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the source map given by [mapping] to disk (if necessary) according to
|
||||||
|
/// [options].
|
||||||
|
///
|
||||||
|
/// The [destination] is the path where the CSS file associated with this source
|
||||||
|
/// map will be written. If it's `null`, that indicates that the CSS will be
|
||||||
|
/// printed to stdout.
|
||||||
///
|
///
|
||||||
/// Returns the source map comment to add to the end of the CSS file.
|
/// Returns the source map comment to add to the end of the CSS file.
|
||||||
String _writeSourceMap(ExecutableOptions options, SingleMapping sourceMap) {
|
String _writeSourceMap(
|
||||||
|
ExecutableOptions options, SingleMapping sourceMap, String destination) {
|
||||||
if (sourceMap == null) return "";
|
if (sourceMap == null) return "";
|
||||||
|
|
||||||
if (!options.writeToStdout) {
|
if (destination != null) {
|
||||||
sourceMap.targetUrl = p.toUri(p.basename(options.destination)).toString();
|
sourceMap.targetUrl = p.toUri(p.basename(destination)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < sourceMap.urls.length; i++) {
|
for (var i = 0; i < sourceMap.urls.length; i++) {
|
||||||
@ -141,7 +161,8 @@ String _writeSourceMap(ExecutableOptions options, SingleMapping sourceMap) {
|
|||||||
// The special URL "" indicates a file that came from stdin.
|
// The special URL "" indicates a file that came from stdin.
|
||||||
if (url == "") continue;
|
if (url == "") continue;
|
||||||
|
|
||||||
sourceMap.urls[i] = options.sourceMapUrl(Uri.parse(url)).toString();
|
sourceMap.urls[i] =
|
||||||
|
options.sourceMapUrl(Uri.parse(url), destination).toString();
|
||||||
}
|
}
|
||||||
var sourceMapText = convert.json
|
var sourceMapText = convert.json
|
||||||
.encode(sourceMap.toJson(includeSourceContents: options.embedSources));
|
.encode(sourceMap.toJson(includeSourceContents: options.embedSources));
|
||||||
@ -150,7 +171,7 @@ String _writeSourceMap(ExecutableOptions options, SingleMapping sourceMap) {
|
|||||||
if (options.embedSourceMap) {
|
if (options.embedSourceMap) {
|
||||||
url = new Uri.dataFromString(sourceMapText, mimeType: 'application/json');
|
url = new Uri.dataFromString(sourceMapText, mimeType: 'application/json');
|
||||||
} else {
|
} else {
|
||||||
var sourceMapPath = options.destination + '.map';
|
var sourceMapPath = destination + '.map';
|
||||||
ensureDir(p.dirname(sourceMapPath));
|
ensureDir(p.dirname(sourceMapPath));
|
||||||
writeFile(sourceMapPath, sourceMapText);
|
writeFile(sourceMapPath, sourceMapText);
|
||||||
|
|
||||||
|
@ -101,9 +101,10 @@ class ExecutableOptions {
|
|||||||
bool get version => _options['version'] as bool;
|
bool get version => _options['version'] as bool;
|
||||||
|
|
||||||
/// Whether to parse the source file with the indented syntax.
|
/// Whether to parse the source file with the indented syntax.
|
||||||
bool get indented =>
|
///
|
||||||
_ifParsed('indented') as bool ??
|
/// This may be `null`, indicating that this should be determined by each
|
||||||
(source != null && p.extension(source) == '.sass');
|
/// stylesheet's extension.
|
||||||
|
bool get indented => _ifParsed('indented') as bool;
|
||||||
|
|
||||||
/// Whether to use ANSI terminal colors.
|
/// Whether to use ANSI terminal colors.
|
||||||
bool get color =>
|
bool get color =>
|
||||||
@ -128,32 +129,33 @@ class ExecutableOptions {
|
|||||||
/// Whether to print the full Dart stack trace on exceptions.
|
/// Whether to print the full Dart stack trace on exceptions.
|
||||||
bool get trace => _options['trace'] as bool;
|
bool get trace => _options['trace'] as bool;
|
||||||
|
|
||||||
/// The entrypoint Sass file, or `null` if the source should be read from
|
/// A map from source paths to the destination paths where the compiled CSS
|
||||||
/// stdin.
|
/// should be written.
|
||||||
String get source {
|
///
|
||||||
_ensureSourceAndDestination();
|
/// A `null` source indicates that a stylesheet should be read from standard
|
||||||
return _source;
|
/// input. A `null` destination indicates that a stylesheet should be written
|
||||||
|
/// to standard output.
|
||||||
|
Map<String, String> get sourcesToDestinations {
|
||||||
|
if (_sourcesToDestinations != null) return _sourcesToDestinations;
|
||||||
|
|
||||||
|
String source;
|
||||||
|
String destination;
|
||||||
|
if (_options['stdin'] as bool) {
|
||||||
|
if (_options.rest.length > 1) _fail("Compile Sass to CSS.");
|
||||||
|
if (_options.rest.isNotEmpty) destination = _options.rest.first;
|
||||||
|
} else if (_options.rest.isEmpty || _options.rest.length > 2) {
|
||||||
|
_fail("Compile Sass to CSS.");
|
||||||
|
} else if (_options.rest.first == '-') {
|
||||||
|
if (_options.rest.length > 1) destination = _options.rest.last;
|
||||||
|
} else {
|
||||||
|
source = _options.rest.first;
|
||||||
|
if (_options.rest.length > 1) destination = _options.rest.last;
|
||||||
|
}
|
||||||
|
_sourcesToDestinations = new Map.unmodifiable({source: destination});
|
||||||
|
return _sourcesToDestinations;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _source;
|
Map<String, String> _sourcesToDestinations;
|
||||||
|
|
||||||
/// Whether to read the source file from stdin rather than a file on disk.
|
|
||||||
bool get readFromStdin => source == null;
|
|
||||||
|
|
||||||
/// The path to which to write the CSS, or `null` if the CSS should be printed
|
|
||||||
/// to stdout.
|
|
||||||
String get destination {
|
|
||||||
_ensureSourceAndDestination();
|
|
||||||
return _destination;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _destination;
|
|
||||||
|
|
||||||
/// Whether to write the output CSS to stdout rather than a file on disk.
|
|
||||||
bool get writeToStdout => destination == null;
|
|
||||||
|
|
||||||
/// Whether [_source] and [_destination] have been parsed from [_options] yet.
|
|
||||||
var _parsedSourceAndDestination = false;
|
|
||||||
|
|
||||||
/// Whether to emit a source map file.
|
/// Whether to emit a source map file.
|
||||||
bool get emitSourceMap {
|
bool get emitSourceMap {
|
||||||
@ -167,7 +169,9 @@ class ExecutableOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (destination != null) return _options['source-map'] as bool;
|
var writeToStdout = sourcesToDestinations.length == 1 &&
|
||||||
|
sourcesToDestinations.values.single == null;
|
||||||
|
if (!writeToStdout) return _options['source-map'] as bool;
|
||||||
|
|
||||||
if (_ifParsed('source-map-urls') == 'relative') {
|
if (_ifParsed('source-map-urls') == 'relative') {
|
||||||
_fail(
|
_fail(
|
||||||
@ -211,28 +215,9 @@ class ExecutableOptions {
|
|||||||
|
|
||||||
ExecutableOptions._(this._options);
|
ExecutableOptions._(this._options);
|
||||||
|
|
||||||
/// Parses [source] and [destination] from [_options] if they haven't been
|
/// Makes [url] absolute or relative (to the directory containing
|
||||||
/// parsed yet.
|
/// [destination]) according to the `source-map-urls` option.
|
||||||
void _ensureSourceAndDestination() {
|
Uri sourceMapUrl(Uri url, String destination) {
|
||||||
if (_parsedSourceAndDestination) return;
|
|
||||||
_parsedSourceAndDestination = true;
|
|
||||||
|
|
||||||
if (_options['stdin'] as bool) {
|
|
||||||
if (_options.rest.length > 1) _fail("Compile Sass to CSS.");
|
|
||||||
if (_options.rest.isNotEmpty) _destination = _options.rest.first;
|
|
||||||
} else if (_options.rest.isEmpty || _options.rest.length > 2) {
|
|
||||||
_fail("Compile Sass to CSS.");
|
|
||||||
} else if (_options.rest.first == '-') {
|
|
||||||
if (_options.rest.length > 1) _destination = _options.rest.last;
|
|
||||||
} else {
|
|
||||||
_source = _options.rest.first;
|
|
||||||
if (_options.rest.length > 1) _destination = _options.rest.last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes [url] absolute or relative (to [dir]) according to the
|
|
||||||
/// `source-map-urls` option.
|
|
||||||
Uri sourceMapUrl(Uri url) {
|
|
||||||
var path = p.fromUri(url);
|
var path = p.fromUri(url);
|
||||||
return p.toUri(_options['source-map-urls'] == 'relative'
|
return p.toUri(_options['source-map-urls'] == 'relative'
|
||||||
? p.relative(path, from: p.dirname(destination))
|
? p.relative(path, from: p.dirname(destination))
|
||||||
|
Loading…
Reference in New Issue
Block a user