2016-05-24 03:11:09 +02:00
|
|
|
// Copyright 2016 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.
|
|
|
|
|
2020-01-24 22:05:04 +01:00
|
|
|
import 'dart:isolate';
|
|
|
|
|
|
|
|
import 'package:path/path.dart' as p;
|
|
|
|
import 'package:stack_trace/stack_trace.dart';
|
|
|
|
import 'package:term_glyph/term_glyph.dart' as term_glyph;
|
|
|
|
|
|
|
|
import 'package:sass/src/exception.dart';
|
|
|
|
import 'package:sass/src/executable/compile_stylesheet.dart';
|
|
|
|
import 'package:sass/src/executable/options.dart';
|
|
|
|
import 'package:sass/src/executable/repl.dart';
|
|
|
|
import 'package:sass/src/executable/watch.dart';
|
|
|
|
import 'package:sass/src/import_cache.dart';
|
|
|
|
import 'package:sass/src/io.dart';
|
2023-05-19 22:22:44 +02:00
|
|
|
import 'package:sass/src/io.dart' as io;
|
2023-03-10 23:24:33 +01:00
|
|
|
import 'package:sass/src/logger/deprecation_handling.dart';
|
2020-01-24 22:05:04 +01:00
|
|
|
import 'package:sass/src/stylesheet_graph.dart';
|
2023-08-02 02:34:45 +02:00
|
|
|
import 'package:sass/src/util/map.dart';
|
2021-11-05 07:05:50 +01:00
|
|
|
import 'package:sass/src/utils.dart';
|
2023-05-10 22:57:32 +02:00
|
|
|
import 'package:sass/src/embedded/executable.dart'
|
|
|
|
// Never load the embedded protocol when compiling to JS.
|
|
|
|
if (dart.library.js) 'package:sass/src/embedded/unavailable.dart'
|
|
|
|
as embedded;
|
2020-01-24 22:05:04 +01:00
|
|
|
|
|
|
|
Future<void> main(List<String> args) async {
|
|
|
|
var printedError = false;
|
|
|
|
|
|
|
|
// Prints [error] to stderr, along with a preceding newline if anything else
|
|
|
|
// has been printed to stderr.
|
|
|
|
//
|
|
|
|
// If [trace] is passed, its terse representation is printed after the error.
|
2021-03-17 03:25:39 +01:00
|
|
|
void printError(String error, StackTrace? stackTrace) {
|
2023-05-19 22:22:44 +02:00
|
|
|
var buffer = StringBuffer();
|
|
|
|
if (printedError) buffer.writeln();
|
2020-01-24 22:05:04 +01:00
|
|
|
printedError = true;
|
2023-05-19 22:22:44 +02:00
|
|
|
buffer.write(error);
|
2020-01-24 22:05:04 +01:00
|
|
|
|
|
|
|
if (stackTrace != null) {
|
2023-05-19 22:22:44 +02:00
|
|
|
buffer.writeln();
|
|
|
|
buffer.writeln();
|
|
|
|
buffer.write(Trace.from(stackTrace).terse.toString().trimRight());
|
2020-01-24 22:05:04 +01:00
|
|
|
}
|
2023-05-19 22:22:44 +02:00
|
|
|
|
|
|
|
io.printError(buffer);
|
2020-01-24 22:05:04 +01:00
|
|
|
}
|
|
|
|
|
2023-08-02 02:34:45 +02:00
|
|
|
if (args case ['--embedded', ...var rest]) {
|
|
|
|
embedded.main(rest);
|
2023-05-10 22:57:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-17 04:51:32 +01:00
|
|
|
ExecutableOptions? options;
|
2020-01-24 22:05:04 +01:00
|
|
|
try {
|
|
|
|
options = ExecutableOptions.parse(args);
|
|
|
|
term_glyph.ascii = !options.unicode;
|
|
|
|
|
|
|
|
if (options.version) {
|
|
|
|
print(await _loadVersion());
|
|
|
|
exitCode = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.interactive) {
|
|
|
|
await repl(options);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:24:33 +01:00
|
|
|
var graph = StylesheetGraph(ImportCache(
|
|
|
|
loadPaths: options.loadPaths,
|
|
|
|
// This logger is only used for handling fatal/future deprecations
|
|
|
|
// during parsing, and is re-used across parses, so we don't want to
|
|
|
|
// limit repetition. A separate DeprecationHandlingLogger is created for
|
|
|
|
// each compilation, which will limit repetition if verbose is not
|
|
|
|
// passed in addition to handling fatal/future deprecations.
|
|
|
|
logger: DeprecationHandlingLogger(options.logger,
|
|
|
|
fatalDeprecations: options.fatalDeprecations,
|
|
|
|
futureDeprecations: options.futureDeprecations,
|
|
|
|
limitRepetition: false)));
|
2020-01-24 22:05:04 +01:00
|
|
|
if (options.watch) {
|
2022-05-21 00:04:21 +02:00
|
|
|
await watch(options, graph);
|
2020-01-24 22:05:04 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-02 02:34:45 +02:00
|
|
|
for (var (source, destination) in options.sourcesToDestinations.pairs) {
|
2020-01-24 22:05:04 +01:00
|
|
|
try {
|
|
|
|
await compileStylesheet(options, graph, source, destination,
|
|
|
|
ifModified: options.update);
|
|
|
|
} on SassException catch (error, stackTrace) {
|
2023-08-02 02:34:45 +02:00
|
|
|
if (destination != null && !options.emitErrorCss) {
|
|
|
|
_tryDelete(destination);
|
|
|
|
}
|
2020-01-24 22:05:04 +01:00
|
|
|
|
|
|
|
printError(error.toString(color: options.color),
|
2021-11-05 07:05:50 +01:00
|
|
|
options.trace ? getTrace(error) ?? stackTrace : null);
|
2020-01-24 22:05:04 +01:00
|
|
|
|
|
|
|
// Exit code 65 indicates invalid data per
|
2023-02-03 23:47:13 +01:00
|
|
|
// https://www.freebsd.org/cgi/man.cgi?query=sysexits.
|
2020-01-24 22:05:04 +01:00
|
|
|
//
|
|
|
|
// We let exitCode 66 take precedence for deterministic behavior.
|
|
|
|
if (exitCode != 66) exitCode = 65;
|
|
|
|
if (options.stopOnError) return;
|
|
|
|
} on FileSystemException catch (error, stackTrace) {
|
2021-03-19 05:43:02 +01:00
|
|
|
var path = error.path;
|
|
|
|
printError(
|
|
|
|
path == null
|
|
|
|
? error.message
|
|
|
|
: "Error reading ${p.relative(path)}: ${error.message}.",
|
2021-11-05 07:05:50 +01:00
|
|
|
options.trace ? getTrace(error) ?? stackTrace : null);
|
2020-01-24 22:05:04 +01:00
|
|
|
|
|
|
|
// Error 66 indicates no input.
|
|
|
|
exitCode = 66;
|
|
|
|
if (options.stopOnError) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} on UsageException catch (error) {
|
|
|
|
print("${error.message}\n");
|
|
|
|
print("Usage: sass <input.scss> [output.css]\n"
|
|
|
|
" sass <input.scss>:<output.css> <input/>:<output/> <dir/>\n");
|
|
|
|
print(ExecutableOptions.usage);
|
|
|
|
exitCode = 64;
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
var buffer = StringBuffer();
|
2023-08-02 02:34:45 +02:00
|
|
|
if (options?.color ?? false) buffer.write('\u001b[31m\u001b[1m');
|
2020-01-24 22:05:04 +01:00
|
|
|
buffer.write('Unexpected exception:');
|
2023-08-02 02:34:45 +02:00
|
|
|
if (options?.color ?? false) buffer.write('\u001b[0m');
|
2020-01-24 22:05:04 +01:00
|
|
|
buffer.writeln();
|
|
|
|
buffer.writeln(error);
|
|
|
|
|
2021-11-05 07:05:50 +01:00
|
|
|
printError(buffer.toString(), getTrace(error) ?? stackTrace);
|
2020-01-24 22:05:04 +01:00
|
|
|
exitCode = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Loads and returns the current version of Sass.
|
|
|
|
Future<String> _loadVersion() async {
|
2021-04-09 22:19:35 +02:00
|
|
|
if (const bool.hasEnvironment('version')) {
|
2021-03-10 02:04:09 +01:00
|
|
|
var version = const String.fromEnvironment('version');
|
|
|
|
if (const bool.fromEnvironment('node')) {
|
|
|
|
version += " compiled with dart2js "
|
|
|
|
"${const String.fromEnvironment('dart-version')}";
|
|
|
|
}
|
|
|
|
return version;
|
2020-01-24 22:05:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var libDir =
|
|
|
|
p.fromUri(await Isolate.resolvePackageUri(Uri.parse('package:sass/')));
|
|
|
|
var pubspec = readFile(p.join(libDir, '..', 'pubspec.yaml'));
|
|
|
|
return pubspec
|
|
|
|
.split("\n")
|
|
|
|
.firstWhere((line) => line.startsWith('version: '))
|
|
|
|
.split(" ")
|
|
|
|
.last;
|
|
|
|
}
|
2023-08-02 02:34:45 +02:00
|
|
|
|
|
|
|
/// Delete [path] if it exists and do nothing otherwise.
|
|
|
|
///
|
|
|
|
/// This is a separate function to work around dart-lang/sdk#53082.
|
|
|
|
void _tryDelete(String path) {
|
|
|
|
try {
|
|
|
|
deleteFile(path);
|
|
|
|
} on FileSystemException {
|
|
|
|
// If the file doesn't exist, that's fine.
|
|
|
|
}
|
|
|
|
}
|