mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-30 04:39:03 +01:00
Merge pull request #252 from sass/quiet
Add a --quiet flag and a Logger class
This commit is contained in:
commit
a5841c99d0
10
CHANGELOG.md
10
CHANGELOG.md
@ -6,6 +6,16 @@
|
||||
* Add a `--load-path` command-line option (alias `-I`) for passing additional
|
||||
paths to search for Sass files to import.
|
||||
|
||||
* Add a `--quiet` command-line option (alias `-q`) for silencing warnings.
|
||||
|
||||
### Dart API
|
||||
|
||||
* Add a `Logger` class that allows users to control how messages are printed by
|
||||
stylesheets.
|
||||
|
||||
* Add a `logger` parameter to `compile()`, `compileAsync()`, `compileString()`,
|
||||
and `compileStringAsync()`.
|
||||
|
||||
### Node JS API
|
||||
|
||||
* Import URLs passed to importers are no longer normalized. For example, if a
|
||||
|
@ -8,18 +8,24 @@ import 'src/callable.dart';
|
||||
import 'src/compile.dart' as c;
|
||||
import 'src/exception.dart';
|
||||
import 'src/importer.dart';
|
||||
import 'src/logger.dart';
|
||||
import 'src/sync_package_resolver.dart';
|
||||
import 'src/visitor/serialize.dart';
|
||||
|
||||
export 'src/callable.dart' show Callable, AsyncCallable;
|
||||
export 'src/importer.dart';
|
||||
export 'src/logger.dart';
|
||||
export 'src/value.dart' show ListSeparator;
|
||||
export 'src/value/external/value.dart';
|
||||
export 'src/visitor/serialize.dart' show OutputStyle;
|
||||
|
||||
/// Loads the Sass file at [path], compiles it to CSS, and returns the result.
|
||||
///
|
||||
/// If [color] is `true`, this will use terminal colors in warnings.
|
||||
/// If [color] is `true`, this will use terminal colors in warnings. It's
|
||||
/// ignored if [logger] is passed.
|
||||
///
|
||||
/// If [logger] is passed, it's used to emit all messages that are generated by
|
||||
/// Sass code. Users may pass custom subclasses of [Logger].
|
||||
///
|
||||
/// Imports are resolved by trying, in order:
|
||||
///
|
||||
@ -45,13 +51,14 @@ export 'src/visitor/serialize.dart' show OutputStyle;
|
||||
/// Throws a [SassException] if conversion fails.
|
||||
String compile(String path,
|
||||
{bool color: false,
|
||||
Logger logger,
|
||||
Iterable<Importer> importers,
|
||||
Iterable<String> loadPaths,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<Callable> functions,
|
||||
OutputStyle style}) {
|
||||
var result = c.compile(path,
|
||||
color: color,
|
||||
logger: logger ?? new Logger.stderr(color: color),
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver,
|
||||
@ -63,8 +70,13 @@ String compile(String path,
|
||||
/// Compiles [source] to CSS and returns the result.
|
||||
///
|
||||
/// If [indented] is `true`, this parses [source] using indented syntax;
|
||||
/// otherwise (and by default) it uses SCSS. If [color] is `true`, this will use
|
||||
/// terminal colors in warnings.
|
||||
/// otherwise (and by default) it uses SCSS.
|
||||
///
|
||||
/// If [color] is `true`, this will use terminal colors in warnings. It's
|
||||
/// ignored if [logger] is passed.
|
||||
///
|
||||
/// If [logger] is passed, it's used to emit all messages that are generated by
|
||||
/// Sass code. Users may pass custom subclasses of [Logger].
|
||||
///
|
||||
/// Imports are resolved by trying, in order:
|
||||
///
|
||||
@ -95,6 +107,7 @@ String compile(String path,
|
||||
String compileString(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<Importer> importers,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
@ -104,7 +117,7 @@ String compileString(String source,
|
||||
url}) {
|
||||
var result = c.compileString(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
logger: logger ?? new Logger.stderr(color: color),
|
||||
importers: importers,
|
||||
packageResolver: packageResolver,
|
||||
loadPaths: loadPaths,
|
||||
@ -122,13 +135,14 @@ String compileString(String source,
|
||||
/// slower, so [compile] should be preferred if possible.
|
||||
Future<String> compileAsync(String path,
|
||||
{bool color: false,
|
||||
Logger logger,
|
||||
Iterable<AsyncImporter> importers,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
Iterable<AsyncCallable> functions,
|
||||
OutputStyle style}) async {
|
||||
var result = await c.compileAsync(path,
|
||||
color: color,
|
||||
logger: logger ?? new Logger.stderr(color: color),
|
||||
importers: importers,
|
||||
loadPaths: loadPaths,
|
||||
packageResolver: packageResolver,
|
||||
@ -145,6 +159,7 @@ Future<String> compileAsync(String path,
|
||||
Future<String> compileStringAsync(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<AsyncImporter> importers,
|
||||
SyncPackageResolver packageResolver,
|
||||
Iterable<String> loadPaths,
|
||||
@ -154,7 +169,7 @@ Future<String> compileStringAsync(String source,
|
||||
url}) async {
|
||||
var result = await c.compileStringAsync(source,
|
||||
indented: indented,
|
||||
color: color,
|
||||
logger: logger ?? new Logger.stderr(color: color),
|
||||
importers: importers,
|
||||
packageResolver: packageResolver,
|
||||
loadPaths: loadPaths,
|
||||
|
@ -2,6 +2,7 @@
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import '../../logger.dart';
|
||||
import '../../parse/media_query.dart';
|
||||
import '../../utils.dart';
|
||||
|
||||
@ -28,8 +29,8 @@ class CssMediaQuery {
|
||||
/// If passed, [url] is the name of the file from which [contents] comes.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
static List<CssMediaQuery> parseList(String contents, {url}) =>
|
||||
new MediaQueryParser(contents, url: url).parse();
|
||||
static List<CssMediaQuery> parseList(String contents, {url, Logger logger}) =>
|
||||
new MediaQueryParser(contents, url: url, logger: logger).parse();
|
||||
|
||||
/// Creates a media query specifies a type and, optionally, features.
|
||||
CssMediaQuery(this.type, {this.modifier, Iterable<String> features})
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../exception.dart';
|
||||
import '../../logger.dart';
|
||||
import '../../parse/scss.dart';
|
||||
import '../../utils.dart';
|
||||
import 'argument.dart';
|
||||
@ -36,8 +37,9 @@ class ArgumentDeclaration implements SassNode {
|
||||
/// If passed, [url] is the name of the file from which [contents] comes.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory ArgumentDeclaration.parse(String contents, {url}) =>
|
||||
new ScssParser("($contents)", url: url).parseArgumentDeclaration();
|
||||
factory ArgumentDeclaration.parse(String contents, {url, Logger logger}) =>
|
||||
new ScssParser("($contents)", url: url, logger: logger)
|
||||
.parseArgumentDeclaration();
|
||||
|
||||
/// Throws a [SassScriptException] if [positional] and [names] aren't valid
|
||||
/// for this argument declaration.
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../../logger.dart';
|
||||
import '../../parse/at_root_query.dart';
|
||||
import '../css.dart';
|
||||
|
||||
@ -54,8 +55,8 @@ class AtRootQuery {
|
||||
/// If passed, [url] is the name of the file from which [contents] comes.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory AtRootQuery.parse(String contents, {url}) =>
|
||||
new AtRootQueryParser(contents, url: url).parse();
|
||||
factory AtRootQuery.parse(String contents, {url, Logger logger}) =>
|
||||
new AtRootQueryParser(contents, url: url, logger: logger).parse();
|
||||
|
||||
/// Returns whether [this] excludes [node].
|
||||
bool excludes(CssParentNode node) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../../../logger.dart';
|
||||
import '../../../parse/sass.dart';
|
||||
import '../../../parse/scss.dart';
|
||||
import '../statement.dart';
|
||||
@ -21,21 +22,19 @@ class Stylesheet extends ParentStatement {
|
||||
|
||||
/// Parses an indented-syntax stylesheet from [contents].
|
||||
///
|
||||
/// If passed, [url] is the name of the file from which [contents] comes. If
|
||||
/// [color] is `true`, this will use terminal colors in warnings.
|
||||
/// If passed, [url] is the name of the file from which [contents] comes.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory Stylesheet.parseSass(String contents, {url, bool color: false}) =>
|
||||
new SassParser(contents, url: url, color: color).parse();
|
||||
factory Stylesheet.parseSass(String contents, {url, Logger logger}) =>
|
||||
new SassParser(contents, url: url, logger: logger).parse();
|
||||
|
||||
/// Parses an SCSS stylesheet from [contents].
|
||||
///
|
||||
/// If passed, [url] is the name of the file from which [contents] comes. If
|
||||
/// [color] is `true`, this will use terminal colors in warnings.
|
||||
/// If passed, [url] is the name of the file from which [contents] comes.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory Stylesheet.parseScss(String contents, {url, bool color: false}) =>
|
||||
new ScssParser(contents, url: url, color: color).parse();
|
||||
factory Stylesheet.parseScss(String contents, {url, Logger logger}) =>
|
||||
new ScssParser(contents, url: url, logger: logger).parse();
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitStylesheet(this);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import '../../extend/functions.dart';
|
||||
import '../../logger.dart';
|
||||
import '../../parse/selector.dart';
|
||||
import '../../utils.dart';
|
||||
import '../../visitor/interface/selector.dart';
|
||||
@ -57,8 +58,9 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent {
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory CompoundSelector.parse(String contents,
|
||||
{url, bool allowParent: true}) =>
|
||||
new SelectorParser(contents, url: url, allowParent: allowParent)
|
||||
{url, Logger logger, bool allowParent: true}) =>
|
||||
new SelectorParser(contents,
|
||||
url: url, logger: logger, allowParent: allowParent)
|
||||
.parseCompoundSelector();
|
||||
|
||||
T accept<T>(SelectorVisitor<T> visitor) =>
|
||||
|
@ -3,6 +3,7 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import '../../extend/functions.dart';
|
||||
import '../../logger.dart';
|
||||
import '../../parse/selector.dart';
|
||||
import '../../utils.dart';
|
||||
import '../../exception.dart';
|
||||
@ -52,8 +53,11 @@ class SelectorList extends Selector {
|
||||
/// selector.
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory SelectorList.parse(String contents, {url, bool allowParent: true}) =>
|
||||
new SelectorParser(contents, url: url, allowParent: allowParent).parse();
|
||||
factory SelectorList.parse(String contents,
|
||||
{url, Logger logger, bool allowParent: true}) =>
|
||||
new SelectorParser(contents,
|
||||
url: url, logger: logger, allowParent: allowParent)
|
||||
.parse();
|
||||
|
||||
T accept<T>(SelectorVisitor<T> visitor) => visitor.visitSelectorList(this);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import '../../exception.dart';
|
||||
import '../../logger.dart';
|
||||
import '../../parse/selector.dart';
|
||||
import '../selector.dart';
|
||||
|
||||
@ -34,8 +35,9 @@ abstract class SimpleSelector extends Selector {
|
||||
///
|
||||
/// Throws a [SassFormatException] if parsing fails.
|
||||
factory SimpleSelector.parse(String contents,
|
||||
{url, bool allowParent: true}) =>
|
||||
new SelectorParser(contents, url: url, allowParent: allowParent)
|
||||
{url, Logger logger, bool allowParent: true}) =>
|
||||
new SelectorParser(contents,
|
||||
url: url, logger: logger, allowParent: allowParent)
|
||||
.parseSimpleSelector();
|
||||
|
||||
/// Returns a new [SimpleSelector] based on [this], as though it had been
|
||||
|
@ -9,6 +9,7 @@ import 'callable.dart';
|
||||
import 'importer.dart';
|
||||
import 'importer/node.dart';
|
||||
import 'io.dart';
|
||||
import 'logger.dart';
|
||||
import 'sync_package_resolver.dart';
|
||||
import 'util/path.dart';
|
||||
import 'visitor/async_evaluate.dart';
|
||||
@ -19,7 +20,7 @@ import 'visitor/serialize.dart';
|
||||
/// node-sass compatible API.
|
||||
CompileResult compile(String path,
|
||||
{bool indented,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<Importer> importers,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -31,7 +32,7 @@ CompileResult compile(String path,
|
||||
LineFeed lineFeed}) =>
|
||||
compileString(readFile(path),
|
||||
indented: indented ?? p.extension(path) == '.sass',
|
||||
color: color,
|
||||
logger: logger,
|
||||
functions: functions,
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
@ -48,7 +49,7 @@ CompileResult compile(String path,
|
||||
/// the node-sass compatible API.
|
||||
CompileResult compileString(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<Importer> importers,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -61,8 +62,8 @@ CompileResult compileString(String source,
|
||||
LineFeed lineFeed,
|
||||
url}) {
|
||||
var sassTree = indented
|
||||
? new Stylesheet.parseSass(source, url: url, color: color)
|
||||
: new Stylesheet.parseScss(source, url: url, color: color);
|
||||
? new Stylesheet.parseSass(source, url: url, logger: logger)
|
||||
: new Stylesheet.parseScss(source, url: url, logger: logger);
|
||||
|
||||
var evaluateResult = evaluate(sassTree,
|
||||
importers: (importers?.toList() ?? [])
|
||||
@ -70,7 +71,7 @@ CompileResult compileString(String source,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color);
|
||||
logger: logger);
|
||||
var css = serialize(evaluateResult.stylesheet,
|
||||
style: style,
|
||||
useSpaces: useSpaces,
|
||||
@ -84,7 +85,7 @@ CompileResult compileString(String source,
|
||||
/// the node-sass compatible API.
|
||||
Future<CompileResult> compileAsync(String path,
|
||||
{bool indented,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<AsyncImporter> importers,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -96,7 +97,7 @@ Future<CompileResult> compileAsync(String path,
|
||||
LineFeed lineFeed}) =>
|
||||
compileStringAsync(readFile(path),
|
||||
indented: indented ?? p.extension(path) == '.sass',
|
||||
color: color,
|
||||
logger: logger,
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
packageResolver: packageResolver,
|
||||
@ -113,7 +114,7 @@ Future<CompileResult> compileAsync(String path,
|
||||
/// support the node-sass compatible API.
|
||||
Future<CompileResult> compileStringAsync(String source,
|
||||
{bool indented: false,
|
||||
bool color: false,
|
||||
Logger logger,
|
||||
Iterable<AsyncImporter> importers,
|
||||
NodeImporter nodeImporter,
|
||||
SyncPackageResolver packageResolver,
|
||||
@ -126,8 +127,8 @@ Future<CompileResult> compileStringAsync(String source,
|
||||
LineFeed lineFeed,
|
||||
url}) async {
|
||||
var sassTree = indented
|
||||
? new Stylesheet.parseSass(source, url: url, color: color)
|
||||
: new Stylesheet.parseScss(source, url: url, color: color);
|
||||
? new Stylesheet.parseSass(source, url: url, logger: logger)
|
||||
: new Stylesheet.parseScss(source, url: url, logger: logger);
|
||||
|
||||
var evaluateResult = await evaluateAsync(sassTree,
|
||||
importers: (importers?.toList() ?? [])
|
||||
@ -135,7 +136,7 @@ Future<CompileResult> compileStringAsync(String source,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color);
|
||||
logger: logger);
|
||||
var css = serialize(evaluateResult.stylesheet,
|
||||
style: style,
|
||||
useSpaces: useSpaces,
|
||||
|
@ -31,6 +31,7 @@ main(List<String> args) async {
|
||||
allowed: ['expanded', 'compressed'],
|
||||
defaultsTo: 'expanded')
|
||||
..addFlag('color', abbr: 'c', help: 'Whether to emit terminal colors.')
|
||||
..addFlag('quiet', abbr: 'q', help: "Don't print warnings.")
|
||||
..addFlag('trace', help: 'Print full Dart stack traces for exceptions.')
|
||||
..addFlag('help',
|
||||
abbr: 'h', help: 'Print this usage information.', negatable: false)
|
||||
@ -67,6 +68,8 @@ main(List<String> args) async {
|
||||
|
||||
var color =
|
||||
options.wasParsed('color') ? options['color'] as bool : hasTerminal;
|
||||
var logger =
|
||||
options['quiet'] as bool ? Logger.quiet : new Logger.stderr(color: color);
|
||||
var style = options['style'] == 'compressed'
|
||||
? OutputStyle.compressed
|
||||
: OutputStyle.expanded;
|
||||
@ -76,17 +79,24 @@ main(List<String> args) async {
|
||||
String css;
|
||||
if (stdinFlag) {
|
||||
css = await _compileStdin(
|
||||
style: style, loadPaths: loadPaths, asynchronous: asynchronous);
|
||||
logger: logger,
|
||||
style: style,
|
||||
loadPaths: loadPaths,
|
||||
asynchronous: asynchronous);
|
||||
} else {
|
||||
var input = options.rest.first;
|
||||
if (input == '-') {
|
||||
css = await _compileStdin(
|
||||
style: style, loadPaths: loadPaths, asynchronous: asynchronous);
|
||||
logger: logger,
|
||||
style: style,
|
||||
loadPaths: loadPaths,
|
||||
asynchronous: asynchronous);
|
||||
} else if (asynchronous) {
|
||||
css = await compileAsync(input,
|
||||
color: color, style: style, loadPaths: loadPaths);
|
||||
logger: logger, style: style, loadPaths: loadPaths);
|
||||
} else {
|
||||
css = compile(input, color: color, style: style, loadPaths: loadPaths);
|
||||
css =
|
||||
compile(input, logger: logger, style: style, loadPaths: loadPaths);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +160,7 @@ Future<String> _loadVersion() async {
|
||||
|
||||
/// Compiles Sass from standard input and returns the result.
|
||||
Future<String> _compileStdin(
|
||||
{bool color: false,
|
||||
{Logger logger,
|
||||
OutputStyle style,
|
||||
List<String> loadPaths,
|
||||
bool asynchronous: false}) async {
|
||||
@ -158,10 +168,10 @@ Future<String> _compileStdin(
|
||||
var importer = new FilesystemImporter('.');
|
||||
if (asynchronous) {
|
||||
return await compileStringAsync(text,
|
||||
color: color, style: style, importer: importer, loadPaths: loadPaths);
|
||||
logger: logger, style: style, importer: importer, loadPaths: loadPaths);
|
||||
} else {
|
||||
return compileString(text,
|
||||
color: color, style: style, importer: importer, loadPaths: loadPaths);
|
||||
logger: logger, style: style, importer: importer, loadPaths: loadPaths);
|
||||
}
|
||||
}
|
||||
|
||||
|
83
lib/src/logger.dart
Normal file
83
lib/src/logger.dart
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2017 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:source_span/source_span.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
import 'io.dart';
|
||||
import 'util/path.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
/// An interface for loggers that print messages produced by Sass stylesheets.
|
||||
///
|
||||
/// This may be implemented by user code.
|
||||
abstract class Logger {
|
||||
/// A logger that silently ignores all messages.
|
||||
static final Logger quiet = new _QuietLogger();
|
||||
|
||||
/// Creates a logger that prints warnings to standard error, with terminal
|
||||
/// colors if [color] is `true` (default `false`).
|
||||
const factory Logger.stderr({bool color}) = _StderrLogger;
|
||||
|
||||
/// Emits a warning with the given [message].
|
||||
///
|
||||
/// If [span] is passed, it's the location in the Sass source that generated
|
||||
/// the warning. If [trace] is passed, it's the Sass stack trace when the
|
||||
/// warning was issued. If [deprecation] is `true`, it indicates that this is
|
||||
/// a deprecation warning. Implementations should surface all this information
|
||||
/// to the end user.
|
||||
void warn(String message,
|
||||
{FileSpan span, Trace trace, bool deprecation: false});
|
||||
|
||||
/// Emits a debugging message associated with the given [span].
|
||||
void debug(String message, SourceSpan span);
|
||||
}
|
||||
|
||||
/// A logger that emits no messages.
|
||||
class _QuietLogger implements Logger {
|
||||
void warn(String message,
|
||||
{FileSpan span, Trace trace, bool deprecation: false}) {}
|
||||
void debug(String message, SourceSpan span) {}
|
||||
}
|
||||
|
||||
/// A logger that prints warnings to standard error.
|
||||
class _StderrLogger implements Logger {
|
||||
/// Whether to use terminal colors in messages.
|
||||
final bool color;
|
||||
|
||||
const _StderrLogger({this.color: false});
|
||||
|
||||
void warn(String message,
|
||||
{FileSpan span, Trace trace, bool deprecation: false}) {
|
||||
if (color) {
|
||||
// Bold yellow.
|
||||
stderr.write('\u001b[33m\u001b[1m');
|
||||
if (deprecation) stderr.write('Deprecation ');
|
||||
stderr.write('Warning\u001b[0m');
|
||||
} else {
|
||||
if (deprecation) stderr.write('DEPRECATION ');
|
||||
stderr.write('WARNING');
|
||||
}
|
||||
|
||||
if (span == null) {
|
||||
stderr.writeln(': $message');
|
||||
} else if (trace != null) {
|
||||
// If there's a span and a trace, the span's location information is
|
||||
// probably duplicated in the trace, so we just use it for highlighting.
|
||||
stderr.writeln(': $message\n\n${span.highlight(color: color)}');
|
||||
} else {
|
||||
stderr.writeln(' on ${span.message("\n" + message, color: color)}');
|
||||
}
|
||||
|
||||
if (trace != null) stderr.writeln(indent(trace.toString().trimRight(), 4));
|
||||
stderr.writeln();
|
||||
}
|
||||
|
||||
void debug(String message, SourceSpan span) {
|
||||
stderr
|
||||
.write('${p.prettyUri(span.start.sourceUrl)}:${span.start.line + 1} ');
|
||||
stderr.write(color ? '\u001b[1mDebug\u001b[0m' : 'DEBUG');
|
||||
stderr.writeln(': $message');
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@
|
||||
import 'package:charcode/charcode.dart';
|
||||
|
||||
import '../ast/sass.dart';
|
||||
import '../logger.dart';
|
||||
import 'parser.dart';
|
||||
|
||||
/// A parser for `@at-root` queries.
|
||||
class AtRootQueryParser extends Parser {
|
||||
AtRootQueryParser(String contents, {url}) : super(contents, url: url);
|
||||
AtRootQueryParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
AtRootQuery parse() {
|
||||
return wrapSpanFormatException(() {
|
||||
|
@ -4,12 +4,14 @@
|
||||
|
||||
import 'package:charcode/charcode.dart';
|
||||
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
import 'parser.dart';
|
||||
|
||||
/// A parser for `@keyframes` block selectors.
|
||||
class KeyframeSelectorParser extends Parser {
|
||||
KeyframeSelectorParser(String contents, {url}) : super(contents, url: url);
|
||||
KeyframeSelectorParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
List<String> parse() {
|
||||
return wrapSpanFormatException(() {
|
||||
|
@ -5,12 +5,14 @@
|
||||
import 'package:charcode/charcode.dart';
|
||||
|
||||
import '../ast/css.dart';
|
||||
import '../logger.dart';
|
||||
import '../utils.dart';
|
||||
import 'parser.dart';
|
||||
|
||||
/// A parser for `@media` queries.
|
||||
class MediaQueryParser extends Parser {
|
||||
MediaQueryParser(String contents, {url}) : super(contents, url: url);
|
||||
MediaQueryParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
List<CssMediaQuery> parse() {
|
||||
return wrapSpanFormatException(() {
|
||||
|
@ -8,6 +8,7 @@ import 'package:source_span/source_span.dart';
|
||||
import 'package:string_scanner/string_scanner.dart';
|
||||
|
||||
import '../exception.dart';
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
|
||||
/// The abstract base class for all parsers.
|
||||
@ -19,8 +20,12 @@ abstract class Parser {
|
||||
/// The scanner that scans through the text being parsed.
|
||||
final SpanScanner scanner;
|
||||
|
||||
Parser(String contents, {url})
|
||||
: scanner = new SpanScanner(contents, sourceUrl: url);
|
||||
/// The logger to use when emitting warnings.
|
||||
final Logger _logger;
|
||||
|
||||
Parser(String contents, {url, Logger logger})
|
||||
: scanner = new SpanScanner(contents, sourceUrl: url),
|
||||
_logger = logger ?? const Logger.stderr();
|
||||
|
||||
// ## Tokens
|
||||
|
||||
@ -560,6 +565,10 @@ abstract class Parser {
|
||||
return scanner.substring(start);
|
||||
}
|
||||
|
||||
/// Prints a warning to standard error, associated with [span].
|
||||
@protected
|
||||
void warn(String message, FileSpan span) => _logger.warn(message, span: span);
|
||||
|
||||
/// Prints a source span highlight of the current location being scanned.
|
||||
///
|
||||
/// If [message] is passed, prints that as well. This is intended for use when
|
||||
|
@ -7,6 +7,7 @@ import 'package:string_scanner/string_scanner.dart';
|
||||
|
||||
import '../ast/sass.dart';
|
||||
import '../interpolation_buffer.dart';
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
import 'stylesheet.dart';
|
||||
|
||||
@ -36,8 +37,8 @@ class SassParser extends StylesheetParser {
|
||||
|
||||
bool get indented => true;
|
||||
|
||||
SassParser(String contents, {url, bool color: false})
|
||||
: super(contents, url: url, color: color);
|
||||
SassParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
Interpolation styleRuleSelector() {
|
||||
var start = scanner.state;
|
||||
|
@ -6,6 +6,7 @@ import 'package:charcode/charcode.dart';
|
||||
|
||||
import '../ast/sass.dart';
|
||||
import '../interpolation_buffer.dart';
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
import 'stylesheet.dart';
|
||||
|
||||
@ -14,8 +15,8 @@ class ScssParser extends StylesheetParser {
|
||||
bool get indented => false;
|
||||
int get currentIndentation => null;
|
||||
|
||||
ScssParser(String contents, {url, bool color: false})
|
||||
: super(contents, url: url, color: color);
|
||||
ScssParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
Interpolation styleRuleSelector() => almostAnyValue();
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'package:charcode/charcode.dart';
|
||||
|
||||
import '../ast/selector.dart';
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
import '../utils.dart';
|
||||
import 'parser.dart';
|
||||
@ -21,9 +22,9 @@ class SelectorParser extends Parser {
|
||||
/// Whether this parser allows the parent selector `&`.
|
||||
final bool _allowParent;
|
||||
|
||||
SelectorParser(String contents, {url, bool allowParent: true})
|
||||
SelectorParser(String contents, {url, Logger logger, bool allowParent: true})
|
||||
: _allowParent = allowParent,
|
||||
super(contents, url: url);
|
||||
super(contents, url: url, logger: logger);
|
||||
|
||||
SelectorList parse() {
|
||||
return wrapSpanFormatException(() {
|
||||
|
@ -14,6 +14,7 @@ import '../ast/sass.dart';
|
||||
import '../exception.dart';
|
||||
import '../color_names.dart';
|
||||
import '../interpolation_buffer.dart';
|
||||
import '../logger.dart';
|
||||
import '../util/character.dart';
|
||||
import '../utils.dart';
|
||||
import '../value.dart';
|
||||
@ -57,12 +58,8 @@ abstract class StylesheetParser extends Parser {
|
||||
/// Whether the parser is currently within a parenthesized expression.
|
||||
var _inParentheses = false;
|
||||
|
||||
/// Whether warnings should be emitted using terminal colors.
|
||||
@protected
|
||||
final bool color;
|
||||
|
||||
StylesheetParser(String contents, {url, this.color: false})
|
||||
: super(contents, url: url);
|
||||
StylesheetParser(String contents, {url, Logger logger})
|
||||
: super(contents, url: url, logger: logger);
|
||||
|
||||
// ## Statements
|
||||
|
||||
@ -227,8 +224,7 @@ abstract class StylesheetParser extends Parser {
|
||||
var children = this.children(_statement);
|
||||
if (indented && children.isEmpty) {
|
||||
warn("This selector doesn't have any properties and won't be rendered.",
|
||||
selectorSpan,
|
||||
color: color);
|
||||
selectorSpan);
|
||||
}
|
||||
|
||||
_inStyleRule = wasInStyleRule;
|
||||
@ -1964,8 +1960,7 @@ abstract class StylesheetParser extends Parser {
|
||||
warn(
|
||||
'In Sass, "&&" means two copies of the parent selector. You '
|
||||
'probably want to use "and" instead.',
|
||||
scanner.spanFrom(start),
|
||||
color: color);
|
||||
scanner.spanFrom(start));
|
||||
scanner.position--;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import 'package:source_span/source_span.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
import 'ast/node.dart';
|
||||
import 'io.dart';
|
||||
import 'util/character.dart';
|
||||
|
||||
/// The URL used in stack traces when no source URL is available.
|
||||
@ -25,6 +24,10 @@ String toSentence(Iterable iter, [String conjunction]) {
|
||||
return iter.take(iter.length - 1).join(", ") + " $conjunction ${iter.last}";
|
||||
}
|
||||
|
||||
/// Returns [string] with every line indented [indentation] spaces.
|
||||
String indent(String string, int indentation) =>
|
||||
string.split("\n").map((line) => (" " * indentation) + line).join("\n");
|
||||
|
||||
/// Returns [name] if [number] is 1, or the plural of [name] otherwise.
|
||||
///
|
||||
/// By default, this just adds "s" to the end of [name] to get the plural. If
|
||||
@ -308,11 +311,3 @@ Future<Map<String, V2>> normalizedMapMapAsync<K, V1, V2>(Map<K, V1> map,
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Prints a warning to standard error, associated with [span].
|
||||
///
|
||||
/// If [color] is `true`, this uses terminal colors.
|
||||
void warn(String message, FileSpan span, {bool color: false}) {
|
||||
var warning = color ? '\u001b[33m\u001b[1mWarning\u001b[0m' : 'WARNING';
|
||||
stderr.writeln("$warning on ${span.message("\n$message", color: color)}\n");
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import '../exception.dart';
|
||||
import '../extend/extender.dart';
|
||||
import '../importer.dart';
|
||||
import '../importer/node.dart';
|
||||
import '../io.dart';
|
||||
import '../logger.dart';
|
||||
import '../parse/keyframe_selector.dart';
|
||||
import '../utils.dart';
|
||||
import '../util/path.dart';
|
||||
@ -40,24 +40,25 @@ typedef Future _ScopeCallback(Future callback());
|
||||
/// If [environment] is passed, it's used as the lexical environment when
|
||||
/// evaluating [stylesheet]. It should only contain global definitions.
|
||||
///
|
||||
/// If [color] is `true`, this will use terminal colors in warnings.
|
||||
///
|
||||
/// If [importer] is passed, it's used to resolve relative imports in
|
||||
/// [stylesheet] relative to `stylesheet.span.sourceUrl`.
|
||||
///
|
||||
/// Warnings are emitted using [logger], or printed to standard error by
|
||||
/// default.
|
||||
///
|
||||
/// Throws a [SassRuntimeException] if evaluation fails.
|
||||
Future<EvaluateResult> evaluateAsync(Stylesheet stylesheet,
|
||||
{Iterable<AsyncImporter> importers,
|
||||
NodeImporter nodeImporter,
|
||||
AsyncImporter importer,
|
||||
Iterable<AsyncCallable> functions,
|
||||
bool color: false}) =>
|
||||
Logger logger}) =>
|
||||
new _EvaluateVisitor(
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color)
|
||||
logger: logger)
|
||||
.run(stylesheet);
|
||||
|
||||
/// A visitor that executes Sass code to produce a CSS tree.
|
||||
@ -72,8 +73,8 @@ class _EvaluateVisitor
|
||||
/// compiled to Node.js.
|
||||
final NodeImporter _nodeImporter;
|
||||
|
||||
/// Whether to use terminal colors in warnings.
|
||||
final bool _color;
|
||||
/// The logger to use to print warnings.
|
||||
final Logger _logger;
|
||||
|
||||
/// The current lexical environment.
|
||||
var _environment = new AsyncEnvironment();
|
||||
@ -163,11 +164,11 @@ class _EvaluateVisitor
|
||||
NodeImporter nodeImporter,
|
||||
AsyncImporter importer,
|
||||
Iterable<AsyncCallable> functions,
|
||||
bool color: false})
|
||||
Logger logger})
|
||||
: _importers = importers == null ? const [] : importers.toList(),
|
||||
_importer = importer ?? Importer.noOp,
|
||||
_nodeImporter = nodeImporter,
|
||||
_color = color {
|
||||
_logger = logger {
|
||||
_environment.setFunction(
|
||||
new BuiltInCallable("global-variable-exists", r"$name", (arguments) {
|
||||
var variable = arguments[0].assertString("name");
|
||||
@ -231,12 +232,11 @@ class _EvaluateVisitor
|
||||
_callableSpan));
|
||||
|
||||
if (function is SassString) {
|
||||
warn(
|
||||
"DEPRECATION WARNING: Passing a string to call() is deprecated and "
|
||||
"will be illegal\n"
|
||||
_warn(
|
||||
"Passing a string to call() is deprecated and will be illegal\n"
|
||||
"in Sass 4.0. Use call(get-function($function)) instead.",
|
||||
_callableSpan,
|
||||
color: _color);
|
||||
deprecation: true);
|
||||
|
||||
var expression = new FunctionExpression(
|
||||
new Interpolation([function.text], _callableSpan), invocation);
|
||||
@ -306,8 +306,8 @@ class _EvaluateVisitor
|
||||
if (node.query != null) {
|
||||
var resolved =
|
||||
await _performInterpolation(node.query, warnForColor: true);
|
||||
query = _adjustParseError(
|
||||
node.query.span, () => new AtRootQuery.parse(resolved));
|
||||
query = _adjustParseError(node.query.span,
|
||||
() => new AtRootQuery.parse(resolved, logger: _logger));
|
||||
}
|
||||
|
||||
var parent = _parent;
|
||||
@ -452,10 +452,9 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
Future<Value> visitDebugRule(DebugRule node) async {
|
||||
var start = node.span.start;
|
||||
var value = await node.expression.accept(this);
|
||||
stderr.writeln("${p.prettyUri(start.sourceUrl)}:${start.line + 1} DEBUG: "
|
||||
"${value is SassString ? value.text : value}");
|
||||
_logger.debug(
|
||||
value is SassString ? value.text : value.toString(), node.span);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -541,7 +540,7 @@ class _EvaluateVisitor
|
||||
var target = _adjustParseError(
|
||||
targetText.span,
|
||||
() => new SimpleSelector.parse(targetText.value.trim(),
|
||||
allowParent: false));
|
||||
logger: _logger, allowParent: false));
|
||||
_extender.addExtension(_styleRule.selector, target, node, _mediaQueries);
|
||||
return null;
|
||||
}
|
||||
@ -753,8 +752,8 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
return url.startsWith('file') && pUrl.extension(url) == '.sass'
|
||||
? new Stylesheet.parseSass(contents, url: url, color: _color)
|
||||
: new Stylesheet.parseScss(contents, url: url, color: _color);
|
||||
? new Stylesheet.parseSass(contents, url: url, logger: _logger)
|
||||
: new Stylesheet.parseScss(contents, url: url, logger: _logger);
|
||||
}
|
||||
|
||||
/// Parses the contents of [result] into a [Stylesheet].
|
||||
@ -773,9 +772,9 @@ class _EvaluateVisitor
|
||||
var displayUrl = url.resolve(p.basename(canonicalUrl.path));
|
||||
return result.isIndented
|
||||
? new Stylesheet.parseSass(result.contents,
|
||||
url: displayUrl, color: _color)
|
||||
url: displayUrl, logger: _logger)
|
||||
: new Stylesheet.parseScss(result.contents,
|
||||
url: displayUrl, color: _color);
|
||||
url: displayUrl, logger: _logger);
|
||||
});
|
||||
}
|
||||
|
||||
@ -898,8 +897,8 @@ class _EvaluateVisitor
|
||||
await _performInterpolation(interpolation, warnForColor: true);
|
||||
|
||||
// TODO(nweiz): Remove this type argument when sdk#31398 is fixed.
|
||||
return _adjustParseError<List<CssMediaQuery>>(
|
||||
interpolation.span, () => CssMediaQuery.parseList(resolved));
|
||||
return _adjustParseError<List<CssMediaQuery>>(interpolation.span,
|
||||
() => CssMediaQuery.parseList(resolved, logger: _logger));
|
||||
}
|
||||
|
||||
/// Returns a list of queries that selects for platforms that match both
|
||||
@ -925,8 +924,10 @@ class _EvaluateVisitor
|
||||
var selectorText = await _interpolationToValue(node.selector,
|
||||
trim: true, warnForColor: true);
|
||||
if (_inKeyframes) {
|
||||
var parsedSelector = _adjustParseError(node.selector.span,
|
||||
() => new KeyframeSelectorParser(selectorText.value).parse());
|
||||
var parsedSelector = _adjustParseError(
|
||||
node.selector.span,
|
||||
() => new KeyframeSelectorParser(selectorText.value, logger: _logger)
|
||||
.parse());
|
||||
var rule = new CssKeyframeBlock(
|
||||
new CssValue(
|
||||
new List.unmodifiable(parsedSelector), node.selector.span),
|
||||
@ -941,8 +942,8 @@ class _EvaluateVisitor
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsedSelector = _adjustParseError(
|
||||
node.selector.span, () => new SelectorList.parse(selectorText.value));
|
||||
var parsedSelector = _adjustParseError(node.selector.span,
|
||||
() => new SelectorList.parse(selectorText.value, logger: _logger));
|
||||
parsedSelector = _addExceptionSpan(
|
||||
node.selector.span,
|
||||
() => parsedSelector.resolveParentSelectors(
|
||||
@ -1055,18 +1056,13 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
Future<Value> visitWarnRule(WarnRule node) async {
|
||||
await _addExceptionSpanAsync(node.span, () async {
|
||||
var value = await node.expression.accept(this);
|
||||
var string = value is SassString
|
||||
? value.text
|
||||
: _serialize(value, node.expression.span);
|
||||
stderr.writeln("WARNING: $string");
|
||||
});
|
||||
|
||||
for (var line in _stackTrace(node.span).toString().split("\n")) {
|
||||
stderr.writeln(" $line");
|
||||
}
|
||||
|
||||
var value = await _addExceptionSpanAsync(
|
||||
node.span, () => node.expression.accept(this));
|
||||
_logger.warn(
|
||||
value is SassString
|
||||
? value.text
|
||||
: _serialize(value, node.expression.span),
|
||||
trace: _stackTrace(node.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1614,14 +1610,14 @@ class _EvaluateVisitor
|
||||
BinaryOperator.plus,
|
||||
new StringExpression(new Interpolation([""], null), quotes: true),
|
||||
expression);
|
||||
warn(
|
||||
_warn(
|
||||
"You probably don't mean to use the color value "
|
||||
"${namesByColor[result]} in interpolation here.\n"
|
||||
"It may end up represented as $result, which will likely produce "
|
||||
"invalid CSS.\n"
|
||||
"Always quote color names when using them as strings or map keys "
|
||||
'(for example, "${namesByColor[result]}").\n'
|
||||
"If you really want to use the color value here, use '$alternative'.\n",
|
||||
"If you really want to use the color value here, use '$alternative'.",
|
||||
expression.span);
|
||||
}
|
||||
|
||||
@ -1729,6 +1725,11 @@ class _EvaluateVisitor
|
||||
return new Trace(frames.reversed);
|
||||
}
|
||||
|
||||
/// Emits a warning with the given [message] about the given [span].
|
||||
void _warn(String message, FileSpan span, {bool deprecation: false}) =>
|
||||
_logger.warn(message,
|
||||
span: span, trace: _stackTrace(span), deprecation: deprecation);
|
||||
|
||||
/// Throws a [SassRuntimeException] with the given [message] and [span].
|
||||
SassRuntimeException _exception(String message, FileSpan span) =>
|
||||
new SassRuntimeException(message, span, _stackTrace(span));
|
||||
|
@ -5,7 +5,7 @@
|
||||
// DO NOT EDIT. This file was generated from async_evaluate.dart.
|
||||
// See tool/synchronize.dart for details.
|
||||
//
|
||||
// Checksum: 53f8eb4f4e6ed2cad3fe359490a50e12911a278a
|
||||
// Checksum: 441fccd1274e61316ec7403a381761fff3052706
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
@ -25,7 +25,7 @@ import '../exception.dart';
|
||||
import '../extend/extender.dart';
|
||||
import '../importer.dart';
|
||||
import '../importer/node.dart';
|
||||
import '../io.dart';
|
||||
import '../logger.dart';
|
||||
import '../parse/keyframe_selector.dart';
|
||||
import '../utils.dart';
|
||||
import '../util/path.dart';
|
||||
@ -44,24 +44,25 @@ typedef void _ScopeCallback(void callback());
|
||||
/// If [environment] is passed, it's used as the lexical environment when
|
||||
/// evaluating [stylesheet]. It should only contain global definitions.
|
||||
///
|
||||
/// If [color] is `true`, this will use terminal colors in warnings.
|
||||
///
|
||||
/// If [importer] is passed, it's used to resolve relative imports in
|
||||
/// [stylesheet] relative to `stylesheet.span.sourceUrl`.
|
||||
///
|
||||
/// Warnings are emitted using [logger], or printed to standard error by
|
||||
/// default.
|
||||
///
|
||||
/// Throws a [SassRuntimeException] if evaluation fails.
|
||||
EvaluateResult evaluate(Stylesheet stylesheet,
|
||||
{Iterable<Importer> importers,
|
||||
NodeImporter nodeImporter,
|
||||
Importer importer,
|
||||
Iterable<Callable> functions,
|
||||
bool color: false}) =>
|
||||
Logger logger}) =>
|
||||
new _EvaluateVisitor(
|
||||
importers: importers,
|
||||
nodeImporter: nodeImporter,
|
||||
importer: importer,
|
||||
functions: functions,
|
||||
color: color)
|
||||
logger: logger)
|
||||
.run(stylesheet);
|
||||
|
||||
/// A visitor that executes Sass code to produce a CSS tree.
|
||||
@ -74,8 +75,8 @@ class _EvaluateVisitor
|
||||
/// compiled to Node.js.
|
||||
final NodeImporter _nodeImporter;
|
||||
|
||||
/// Whether to use terminal colors in warnings.
|
||||
final bool _color;
|
||||
/// The logger to use to print warnings.
|
||||
final Logger _logger;
|
||||
|
||||
/// The current lexical environment.
|
||||
var _environment = new Environment();
|
||||
@ -165,11 +166,11 @@ class _EvaluateVisitor
|
||||
NodeImporter nodeImporter,
|
||||
Importer importer,
|
||||
Iterable<Callable> functions,
|
||||
bool color: false})
|
||||
Logger logger})
|
||||
: _importers = importers == null ? const [] : importers.toList(),
|
||||
_importer = importer ?? Importer.noOp,
|
||||
_nodeImporter = nodeImporter,
|
||||
_color = color {
|
||||
_logger = logger {
|
||||
_environment.setFunction(
|
||||
new BuiltInCallable("global-variable-exists", r"$name", (arguments) {
|
||||
var variable = arguments[0].assertString("name");
|
||||
@ -233,12 +234,11 @@ class _EvaluateVisitor
|
||||
_callableSpan));
|
||||
|
||||
if (function is SassString) {
|
||||
warn(
|
||||
"DEPRECATION WARNING: Passing a string to call() is deprecated and "
|
||||
"will be illegal\n"
|
||||
_warn(
|
||||
"Passing a string to call() is deprecated and will be illegal\n"
|
||||
"in Sass 4.0. Use call(get-function($function)) instead.",
|
||||
_callableSpan,
|
||||
color: _color);
|
||||
deprecation: true);
|
||||
|
||||
var expression = new FunctionExpression(
|
||||
new Interpolation([function.text], _callableSpan), invocation);
|
||||
@ -307,8 +307,8 @@ class _EvaluateVisitor
|
||||
var query = AtRootQuery.defaultQuery;
|
||||
if (node.query != null) {
|
||||
var resolved = _performInterpolation(node.query, warnForColor: true);
|
||||
query = _adjustParseError(
|
||||
node.query.span, () => new AtRootQuery.parse(resolved));
|
||||
query = _adjustParseError(node.query.span,
|
||||
() => new AtRootQuery.parse(resolved, logger: _logger));
|
||||
}
|
||||
|
||||
var parent = _parent;
|
||||
@ -452,10 +452,9 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
Value visitDebugRule(DebugRule node) {
|
||||
var start = node.span.start;
|
||||
var value = node.expression.accept(this);
|
||||
stderr.writeln("${p.prettyUri(start.sourceUrl)}:${start.line + 1} DEBUG: "
|
||||
"${value is SassString ? value.text : value}");
|
||||
_logger.debug(
|
||||
value is SassString ? value.text : value.toString(), node.span);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -538,7 +537,7 @@ class _EvaluateVisitor
|
||||
var target = _adjustParseError(
|
||||
targetText.span,
|
||||
() => new SimpleSelector.parse(targetText.value.trim(),
|
||||
allowParent: false));
|
||||
logger: _logger, allowParent: false));
|
||||
_extender.addExtension(_styleRule.selector, target, node, _mediaQueries);
|
||||
return null;
|
||||
}
|
||||
@ -746,8 +745,8 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
return url.startsWith('file') && pUrl.extension(url) == '.sass'
|
||||
? new Stylesheet.parseSass(contents, url: url, color: _color)
|
||||
: new Stylesheet.parseScss(contents, url: url, color: _color);
|
||||
? new Stylesheet.parseSass(contents, url: url, logger: _logger)
|
||||
: new Stylesheet.parseScss(contents, url: url, logger: _logger);
|
||||
}
|
||||
|
||||
/// Parses the contents of [result] into a [Stylesheet].
|
||||
@ -766,9 +765,9 @@ class _EvaluateVisitor
|
||||
var displayUrl = url.resolve(p.basename(canonicalUrl.path));
|
||||
return result.isIndented
|
||||
? new Stylesheet.parseSass(result.contents,
|
||||
url: displayUrl, color: _color)
|
||||
url: displayUrl, logger: _logger)
|
||||
: new Stylesheet.parseScss(result.contents,
|
||||
url: displayUrl, color: _color);
|
||||
url: displayUrl, logger: _logger);
|
||||
});
|
||||
}
|
||||
|
||||
@ -889,8 +888,8 @@ class _EvaluateVisitor
|
||||
var resolved = _performInterpolation(interpolation, warnForColor: true);
|
||||
|
||||
// TODO(nweiz): Remove this type argument when sdk#31398 is fixed.
|
||||
return _adjustParseError<List<CssMediaQuery>>(
|
||||
interpolation.span, () => CssMediaQuery.parseList(resolved));
|
||||
return _adjustParseError<List<CssMediaQuery>>(interpolation.span,
|
||||
() => CssMediaQuery.parseList(resolved, logger: _logger));
|
||||
}
|
||||
|
||||
/// Returns a list of queries that selects for platforms that match both
|
||||
@ -915,8 +914,10 @@ class _EvaluateVisitor
|
||||
var selectorText =
|
||||
_interpolationToValue(node.selector, trim: true, warnForColor: true);
|
||||
if (_inKeyframes) {
|
||||
var parsedSelector = _adjustParseError(node.selector.span,
|
||||
() => new KeyframeSelectorParser(selectorText.value).parse());
|
||||
var parsedSelector = _adjustParseError(
|
||||
node.selector.span,
|
||||
() => new KeyframeSelectorParser(selectorText.value, logger: _logger)
|
||||
.parse());
|
||||
var rule = new CssKeyframeBlock(
|
||||
new CssValue(
|
||||
new List.unmodifiable(parsedSelector), node.selector.span),
|
||||
@ -931,8 +932,8 @@ class _EvaluateVisitor
|
||||
return null;
|
||||
}
|
||||
|
||||
var parsedSelector = _adjustParseError(
|
||||
node.selector.span, () => new SelectorList.parse(selectorText.value));
|
||||
var parsedSelector = _adjustParseError(node.selector.span,
|
||||
() => new SelectorList.parse(selectorText.value, logger: _logger));
|
||||
parsedSelector = _addExceptionSpan(
|
||||
node.selector.span,
|
||||
() => parsedSelector.resolveParentSelectors(
|
||||
@ -1044,18 +1045,13 @@ class _EvaluateVisitor
|
||||
}
|
||||
|
||||
Value visitWarnRule(WarnRule node) {
|
||||
_addExceptionSpan(node.span, () {
|
||||
var value = node.expression.accept(this);
|
||||
var string = value is SassString
|
||||
? value.text
|
||||
: _serialize(value, node.expression.span);
|
||||
stderr.writeln("WARNING: $string");
|
||||
});
|
||||
|
||||
for (var line in _stackTrace(node.span).toString().split("\n")) {
|
||||
stderr.writeln(" $line");
|
||||
}
|
||||
|
||||
var value =
|
||||
_addExceptionSpan(node.span, () => node.expression.accept(this));
|
||||
_logger.warn(
|
||||
value is SassString
|
||||
? value.text
|
||||
: _serialize(value, node.expression.span),
|
||||
trace: _stackTrace(node.span));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1587,14 +1583,14 @@ class _EvaluateVisitor
|
||||
BinaryOperator.plus,
|
||||
new StringExpression(new Interpolation([""], null), quotes: true),
|
||||
expression);
|
||||
warn(
|
||||
_warn(
|
||||
"You probably don't mean to use the color value "
|
||||
"${namesByColor[result]} in interpolation here.\n"
|
||||
"It may end up represented as $result, which will likely produce "
|
||||
"invalid CSS.\n"
|
||||
"Always quote color names when using them as strings or map keys "
|
||||
'(for example, "${namesByColor[result]}").\n'
|
||||
"If you really want to use the color value here, use '$alternative'.\n",
|
||||
"If you really want to use the color value here, use '$alternative'.",
|
||||
expression.span);
|
||||
}
|
||||
|
||||
@ -1697,6 +1693,11 @@ class _EvaluateVisitor
|
||||
return new Trace(frames.reversed);
|
||||
}
|
||||
|
||||
/// Emits a warning with the given [message] about the given [span].
|
||||
void _warn(String message, FileSpan span, {bool deprecation: false}) =>
|
||||
_logger.warn(message,
|
||||
span: span, trace: _stackTrace(span), deprecation: deprecation);
|
||||
|
||||
/// Throws a [SassRuntimeException] with the given [message] and [span].
|
||||
SassRuntimeException _exception(String message, FileSpan span) =>
|
||||
new SassRuntimeException(message, span, _stackTrace(span));
|
||||
|
@ -168,6 +168,40 @@ void sharedTests(Future<TestProcess> runSass(Iterable<String> arguments)) {
|
||||
await sass.shouldExit(0);
|
||||
});
|
||||
|
||||
group("with --quiet", () {
|
||||
test("doesn't emit @warn", () async {
|
||||
await d.file("test.scss", "@warn heck").create();
|
||||
|
||||
var sass = await runSass(["--quiet", "test.scss"]);
|
||||
expect(sass.stderr, emitsDone);
|
||||
await sass.shouldExit(0);
|
||||
});
|
||||
|
||||
test("doesn't emit @debug", () async {
|
||||
await d.file("test.scss", "@debug heck").create();
|
||||
|
||||
var sass = await runSass(["--quiet", "test.scss"]);
|
||||
expect(sass.stderr, emitsDone);
|
||||
await sass.shouldExit(0);
|
||||
});
|
||||
|
||||
test("doesn't emit parser warnings", () async {
|
||||
await d.file("test.scss", "a {b: c && d}").create();
|
||||
|
||||
var sass = await runSass(["--quiet", "test.scss"]);
|
||||
expect(sass.stderr, emitsDone);
|
||||
await sass.shouldExit(0);
|
||||
});
|
||||
|
||||
test("doesn't emit runner warnings", () async {
|
||||
await d.file("test.scss", "#{blue} {x: y}").create();
|
||||
|
||||
var sass = await runSass(["--quiet", "test.scss"]);
|
||||
expect(sass.stderr, emitsDone);
|
||||
await sass.shouldExit(0);
|
||||
});
|
||||
});
|
||||
|
||||
group("reports errors", () {
|
||||
test("from invalid arguments", () async {
|
||||
var sass = await runSass(["--asdf"]);
|
||||
|
128
test/dart_api/logger_test.dart
Normal file
128
test/dart_api/logger_test.dart
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
@TestOn('vm')
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
import 'package:sass/sass.dart';
|
||||
|
||||
main() {
|
||||
group("with @warn", () {
|
||||
test("passes the message and stack trace to the logger", () {
|
||||
var mustBeCalled = expectAsync0(() {});
|
||||
compileString('''
|
||||
@mixin foo {@warn heck}
|
||||
@include foo;
|
||||
''', logger:
|
||||
new _TestLogger.withWarn((message, {span, trace, deprecation}) {
|
||||
expect(message, equals("heck"));
|
||||
expect(span, isNull);
|
||||
expect(trace.frames.first.member, equals('foo()'));
|
||||
expect(deprecation, isFalse);
|
||||
mustBeCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
test("stringifies the argument", () {
|
||||
var mustBeCalled = expectAsync0(() {});
|
||||
compileString('@warn #abc', logger:
|
||||
new _TestLogger.withWarn((message, {span, trace, deprecation}) {
|
||||
expect(message, equals("#abc"));
|
||||
mustBeCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
test("doesn't inspect the argument", () {
|
||||
var mustBeCalled = expectAsync0(() {});
|
||||
compileString('@warn null', logger:
|
||||
new _TestLogger.withWarn((message, {span, trace, deprecation}) {
|
||||
expect(message, isEmpty);
|
||||
mustBeCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
group("with @debug", () {
|
||||
test("passes the message and span to the logger", () {
|
||||
compileString('@debug heck',
|
||||
logger: new _TestLogger.withDebug(expectAsync2((message, span) {
|
||||
expect(message, equals("heck"));
|
||||
expect(span.start.line, equals(0));
|
||||
expect(span.start.column, equals(0));
|
||||
expect(span.end.line, equals(0));
|
||||
expect(span.end.column, equals(11));
|
||||
})));
|
||||
});
|
||||
|
||||
test("stringifies the argument", () {
|
||||
compileString('@debug #abc',
|
||||
logger: new _TestLogger.withDebug(expectAsync2((message, span) {
|
||||
expect(message, equals("#abc"));
|
||||
})));
|
||||
});
|
||||
|
||||
test("inspects the argument", () {
|
||||
compileString('@debug null',
|
||||
logger: new _TestLogger.withDebug(expectAsync2((message, span) {
|
||||
expect(message, equals("null"));
|
||||
})));
|
||||
});
|
||||
});
|
||||
|
||||
test("with a parser warning passes the message and span", () {
|
||||
var mustBeCalled = expectAsync0(() {});
|
||||
compileString('a {b: c && d}',
|
||||
logger: new _TestLogger.withWarn((message, {span, trace, deprecation}) {
|
||||
expect(message, contains('"&&" means two copies'));
|
||||
|
||||
expect(span.start.line, equals(0));
|
||||
expect(span.start.column, equals(8));
|
||||
expect(span.end.line, equals(0));
|
||||
expect(span.end.column, equals(10));
|
||||
|
||||
expect(trace, isNull);
|
||||
expect(deprecation, isFalse);
|
||||
mustBeCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
test("with a runner warning passes the message, span, and trace", () {
|
||||
var mustBeCalled = expectAsync0(() {});
|
||||
compileString('''
|
||||
@mixin foo {#{blue} {x: y}}
|
||||
@include foo;
|
||||
''',
|
||||
logger: new _TestLogger.withWarn((message, {span, trace, deprecation}) {
|
||||
expect(message, contains("color value blue"));
|
||||
|
||||
expect(span.start.line, equals(0));
|
||||
expect(span.start.column, equals(22));
|
||||
expect(span.end.line, equals(0));
|
||||
expect(span.end.column, equals(26));
|
||||
|
||||
expect(trace.frames.first.member, equals('foo()'));
|
||||
expect(deprecation, isFalse);
|
||||
mustBeCalled();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/// A [Logger] whose [warn] and [debug] methods are provided by callbacks.
|
||||
class _TestLogger implements Logger {
|
||||
final void Function(String, {FileSpan span, Trace trace, bool deprecation})
|
||||
_warn;
|
||||
final void Function(String, SourceSpan) _debug;
|
||||
|
||||
_TestLogger.withWarn(this._warn) : _debug = const Logger.stderr().debug;
|
||||
|
||||
_TestLogger.withDebug(this._debug) : _warn = const Logger.stderr().warn;
|
||||
|
||||
void warn(String message,
|
||||
{FileSpan span, Trace trace, bool deprecation: false}) =>
|
||||
_warn(message, span: span, trace: trace, deprecation: deprecation);
|
||||
void debug(String message, SourceSpan span) => _debug(message, span);
|
||||
}
|
Loading…
Reference in New Issue
Block a user