Merge branch 'master' into feature.hwb

This commit is contained in:
Natalie Weizenbaum 2020-10-28 16:22:18 -07:00
commit 093ff58673
17 changed files with 120 additions and 181 deletions

View File

@ -74,9 +74,10 @@ jobs:
# They next need to be rotated April 2021. See
# https://github.com/nodejs/Release.
- &node-tests
name: Node tests | Dart stable | Node stable
# Run on Node 14 until laverdet/node-fibers#445 is fixed.
name: Node tests | Dart stable | Node 14
language: node_js
node_js: node
node_js: v14
install: pub run grinder before-test
script: tool/travis/task/node_tests.sh
- <<: *node-tests
@ -90,7 +91,7 @@ jobs:
- <<: *node-tests
os: osx
- <<: *node-tests
name: Node tests | Dart dev | Node stable
name: Node tests | Dart dev | Node 14
env: DART_CHANNEL=dev
# Miscellaneous checks.
@ -181,6 +182,20 @@ jobs:
skip_cleanup: true
on: {tags: true}
# Deploy to Chocolatey.
- name: Chocolatey
if: *deploy-if
os: windows
env:
# CHOCOLATEY_TOKEN="..."
- secure: "EnII3YAGESEl9g9rQDcrL1Sw9eww80VJP0qZHz8Da07GB0hUrDQBJ372Zs2A4qaH/GOm7cknszEPOnkE4w3IBwe5idj31Q+WJbcvqqAB1gex3bLYyStdHeohculqmPgpuEPD3yVT59viJIn6L9+GEKNtnCygDpgxMilXzDIXi6vtLqovJc6q09i7XCSnf2IVjzKv0VBSUV0lU9QOZui5/zLN0sCSzE8QKYj0QSoQ8Th3ZTuWn3/CtRYhIaw4/12oepHyXRvieMeNGnhv8O4d1lAOiXKn28COJWA+xvCOZSxIrBCc0k8VzanYftTcp1Zf0Lxkm0ObmFXWaoHATFWjkvW6G34kQrzRpUlWUMmxIxBukHc2ZFuGnVi6pL9ANI8BVh6m8M1ojRqtKCFvBbgDDdxD7qqBQSfdtssEL+m6O0U9A5/xnQxHPbuyL/Y9ww9p/ohEFaaF3MK/qjiWKQJR+TXspmNDBhFC+w2vQ6zetEx787V6POS8ma5MX1+WWOecDtaDuEMv4bzjkTuYk9tuBC4GR/KrdUNbFtcNXCk5To4Du4FBdOW/+yoVg+ZHtgOzSDehgMJMeFM2fTYNt55iwmjwDyS4XcqsWoHHCDMEhIcuXL5XV5VBENFgs98EGofja70Lp05oLH2W7100OuyG0H18lpECx15OXSFnHQh+91g="
script: skip
deploy:
provider: script
script: pub run grinder pkg-chocolatey-deploy
skip_cleanup: true
on: {tags: true}
# Redeploy sass-lang.com.
- name: sass-lang.com
if: *deploy-if

View File

@ -26,6 +26,18 @@
[HWB]: https://en.wikipedia.org/wiki/HWB_color_model
## 1.27.1
* **Potentially breaking bug fix:** `meta.load-css()` now correctly uses the
name `$url` for its first argument, rather than `$module`.
* Don't crash when using `Infinity` or `NaN` as a key in a map.
* Emit a proper parse error for a `=` with no right-hand side in a function.
* Avoid going exponential on certain recursive `@extend` edge cases.
>>>>>>> master
## 1.27.0
* Adds an overload to `map.merge()` that supports merging a nested map.

View File

@ -684,10 +684,20 @@ class Extender {
.toList();
});
return unifiedPaths
.where((complexes) => complexes != null)
.expand((l) => l)
.toList();
// If we're preserving the original selector, mark the first unification as
// such so [_trim] doesn't get rid of it.
var isOriginal = (ComplexSelector _) => false;
if (inOriginal && _mode != ExtendMode.replace) {
var original = unifiedPaths.first.first;
isOriginal = (complex) => complex == original;
}
return _trim(
unifiedPaths
.where((complexes) => complexes != null)
.expand((l) => l)
.toList(),
isOriginal);
}
Iterable<List<Extension>> _extendSimple(

View File

@ -7,6 +7,9 @@ import 'dart:convert';
import 'dart:js_util';
import 'package:js/js.dart';
import 'package:node_interop/fs.dart';
import 'package:node_interop/node_interop.dart';
import 'package:node_interop/stream.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'package:watcher/watcher.dart';
@ -14,56 +17,6 @@ import 'package:watcher/watcher.dart';
import '../exception.dart';
import '../node/chokidar.dart';
@JS()
class _FS {
external Object readFileSync(String path, [String encoding]);
external void writeFileSync(String path, String data);
external bool existsSync(String path);
external void mkdirSync(String path);
external _Stat statSync(String path);
external void unlinkSync(String path);
external List<Object> readdirSync(String path);
}
@JS()
class _Stat {
external bool isFile();
external bool isDirectory();
external _Date get mtime;
}
@JS()
class _Date {
external int getTime();
}
@JS()
class _Stderr {
external void write(String text);
}
@JS()
class _Stdin {
external String read();
external void on(String event, void callback([Object object]));
}
@JS()
class _SystemError {
external String get message;
external String get code;
external String get syscall;
external String get path;
}
@JS()
class _Process {
external String get platform;
external Object get env;
external String cwd();
}
class FileSystemException {
final String message;
final String path;
@ -74,7 +27,7 @@ class FileSystemException {
}
class Stderr {
final _Stderr _stderr;
final Writable _stderr;
Stderr(this._stderr);
@ -87,12 +40,6 @@ class Stderr {
void flush() {}
}
@JS("fs")
external _FS get _fs;
@JS("process")
external _Process get _process;
String readFile(String path) {
// TODO(nweiz): explicitly decode the bytes as UTF-8 like we do in the VM when
// it doesn't cause a substantial performance degradation for large files. See
@ -112,13 +59,13 @@ String readFile(String path) {
/// Wraps `fs.readFileSync` to throw a [FileSystemException].
Object _readFile(String path, [String encoding]) =>
_systemErrorToFileSystemException(() => _fs.readFileSync(path, encoding));
_systemErrorToFileSystemException(() => fs.readFileSync(path, encoding));
void writeFile(String path, String contents) =>
_systemErrorToFileSystemException(() => _fs.writeFileSync(path, contents));
_systemErrorToFileSystemException(() => fs.writeFileSync(path, contents));
void deleteFile(String path) =>
_systemErrorToFileSystemException(() => _fs.unlinkSync(path));
_systemErrorToFileSystemException(() => fs.unlinkSync(path));
Future<String> readStdin() async {
var completer = Completer<String>();
@ -129,16 +76,16 @@ Future<String> readStdin() async {
});
// Node defaults all buffers to 'utf8'.
var sink = utf8.decoder.startChunkedConversion(innerSink);
_stdin.on('data', allowInterop(([chunk]) {
process.stdin.on('data', allowInterop(([Object chunk]) {
assert(chunk != null);
sink.add(chunk as List<int>);
}));
_stdin.on('end', allowInterop(([_]) {
process.stdin.on('end', allowInterop(([Object _]) {
// Callback for 'end' receives no args.
assert(_ == null);
sink.close();
}));
_stdin.on('error', allowInterop(([e]) {
process.stdin.on('error', allowInterop(([Object e]) {
assert(e != null);
stderr.writeln('Failed to read from stdin');
stderr.writeln(e);
@ -148,7 +95,7 @@ Future<String> readStdin() async {
}
/// Cleans up a Node system error's message.
String _cleanErrorMessage(_SystemError error) {
String _cleanErrorMessage(JsSystemError error) {
// The error message is of the form "$code: $text, $syscall '$path'". We just
// want the text.
return error.message.substring("${error.code}: ".length,
@ -161,12 +108,12 @@ bool fileExists(String path) {
// whether the entity in question is a file or a directory. Since false
// negatives are much more common than false positives, it works out in our
// favor to check this first.
if (!_fs.existsSync(path)) return false;
if (!fs.existsSync(path)) return false;
try {
return _fs.statSync(path).isFile();
return fs.statSync(path).isFile();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
@ -179,12 +126,12 @@ bool dirExists(String path) {
// whether the entity in question is a file or a directory. Since false
// negatives are much more common than false positives, it works out in our
// favor to check this first.
if (!_fs.existsSync(path)) return false;
if (!fs.existsSync(path)) return false;
try {
return _fs.statSync(path).isDirectory();
return fs.statSync(path).isDirectory();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
@ -194,13 +141,13 @@ bool dirExists(String path) {
void ensureDir(String path) {
return _systemErrorToFileSystemException(() {
try {
_fs.mkdirSync(path);
fs.mkdirSync(path);
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
if (systemError.code == 'EEXIST') return;
if (systemError.code != 'ENOENT') rethrow;
ensureDir(p.dirname(path));
_fs.mkdirSync(path);
fs.mkdirSync(path);
}
});
}
@ -208,13 +155,13 @@ void ensureDir(String path) {
Iterable<String> listDir(String path, {bool recursive = false}) {
return _systemErrorToFileSystemException(() {
if (!recursive) {
return _fs
return fs
.readdirSync(path)
.map((child) => p.join(path, child as String))
.where((child) => !dirExists(child));
} else {
Iterable<String> list(String parent) =>
_fs.readdirSync(parent).expand((child) {
fs.readdirSync(parent).expand((child) {
var path = p.join(parent, child as String);
return dirExists(path) ? list(path) : [path];
});
@ -225,55 +172,42 @@ Iterable<String> listDir(String path, {bool recursive = false}) {
}
DateTime modificationTime(String path) =>
_systemErrorToFileSystemException(() => DateTime.fromMillisecondsSinceEpoch(
_fs.statSync(path).mtime.getTime()));
_systemErrorToFileSystemException(() =>
DateTime.fromMillisecondsSinceEpoch(fs.statSync(path).mtime.getTime()));
String getEnvironmentVariable(String name) =>
getProperty(_process.env, name) as String;
getProperty(process.env, name) as String;
/// Runs callback and converts any [_SystemError]s it throws into
/// Runs callback and converts any [JsSystemError]s it throws into
/// [FileSystemException]s.
T _systemErrorToFileSystemException<T>(T callback()) {
try {
return callback();
} catch (error) {
var systemError = error as _SystemError;
var systemError = error as JsSystemError;
throw FileSystemException._(
_cleanErrorMessage(systemError), systemError.path);
}
}
@JS("process.stderr")
external _Stderr get _stderr;
final stderr = Stderr(process.stderr);
final stderr = Stderr(_stderr);
bool get hasTerminal => process.stdout.isTTY ?? false;
@JS("process.stdin")
external _Stdin get _stdin;
bool get isWindows => process.platform == 'win32';
bool get hasTerminal => _hasTerminal ?? false;
bool get isWindows => _process.platform == 'win32';
bool get isMacOS => _process.platform == 'darwin';
bool get isMacOS => process.platform == 'darwin';
bool get isNode => true;
// Node seems to support ANSI escapes on all terminals.
bool get supportsAnsiEscapes => hasTerminal;
String get currentPath => _process.cwd();
String get currentPath => process.cwd();
@JS("process.stdout.isTTY")
external bool get _hasTerminal;
int get exitCode => process.exitCode;
@JS("process.exitCode")
external int get exitCode;
// TODO(nweiz): remove this ignore when dart-lang/sdk#39250 is fixed.
// ignore: inference_failure_on_function_return_type
@JS("process.exitCode")
external set exitCode(int code);
set exitCode(int code) => process.exitCode = code;
Future<Stream<WatchEvent>> watchDir(String path, {bool poll = false}) {
var watcher = chokidar.watch(

View File

@ -8,6 +8,7 @@ import 'dart:js_util';
import 'dart:typed_data';
import 'package:js/js.dart';
import 'package:node_interop/js.dart';
import 'package:path/path.dart' as p;
import 'package:tuple/tuple.dart';
@ -17,7 +18,6 @@ import 'compile.dart';
import 'exception.dart';
import 'io.dart';
import 'importer/node.dart';
import 'node/error.dart';
import 'node/exports.dart';
import 'node/function.dart';
import 'node/render_context.dart';
@ -65,13 +65,13 @@ void main() {
///
/// [render]: https://github.com/sass/node-sass#options
void _render(
RenderOptions options, void callback(JSError error, RenderResult result)) {
RenderOptions options, void callback(JsError error, RenderResult result)) {
if (options.fiber != null) {
options.fiber.call(allowInterop(() {
try {
callback(null, _renderSync(options));
} catch (error) {
callback(error as JSError, null);
callback(error as JsError, null);
}
return null;
})).run();
@ -166,8 +166,8 @@ RenderResult _renderSync(RenderOptions options) {
throw "unreachable";
}
/// Converts an exception to a [JSError].
JSError _wrapException(Object exception) {
/// Converts an exception to a [JsError].
JsError _wrapException(Object exception) {
if (exception is SassException) {
return _newRenderError(exception.toString().replaceFirst("Error: ", ""),
line: exception.span.start.line + 1,
@ -177,7 +177,7 @@ JSError _wrapException(Object exception) {
: p.fromUri(exception.span.sourceUrl),
status: 1);
} else {
return JSError(exception.toString());
return JsError(exception.toString());
}
}
@ -388,11 +388,11 @@ bool _enableSourceMaps(RenderOptions options) =>
options.sourceMap is String ||
(isTruthy(options.sourceMap) && options.outFile != null);
/// Creates a [JSError] with the given fields added to it so it acts like a Node
/// Creates a [JsError] with the given fields added to it so it acts like a Node
/// Sass error.
JSError _newRenderError(String message,
JsError _newRenderError(String message,
{int line, int column, String file, int status}) {
var error = JSError(message);
var error = JsError(message);
setProperty(error, 'formatted', 'Error: $message');
if (line != null) setProperty(error, 'line', line);
if (column != null) setProperty(error, 'column', column);

View File

@ -1,11 +0,0 @@
// 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.
import 'package:js/js.dart';
@JS("Error")
class JSError {
external String get message;
external JSError(String message);
}

View File

@ -1640,8 +1640,6 @@ relase. For details, see http://bit.ly/moz-document.
List<Expression> commaExpressions;
Expression singleEqualsOperand;
List<Expression> spaceExpressions;
// Operators whose right-hand operands are not fully parsed yet, in order of
@ -1718,7 +1716,9 @@ relase. For details, see http://bit.ly/moz-document.
}
void addOperator(BinaryOperator operator) {
if (plainCss && operator != BinaryOperator.dividedBy) {
if (plainCss &&
operator != BinaryOperator.dividedBy &&
operator != BinaryOperator.singleEquals) {
scanner.error("Operators aren't allowed in plain CSS.",
position: scanner.position - operator.operator.length,
length: operator.operator.length);
@ -1750,12 +1750,6 @@ relase. For details, see http://bit.ly/moz-document.
ListExpression(spaceExpressions, ListSeparator.space);
spaceExpressions = null;
}
if (singleEqualsOperand != null) {
singleExpression = BinaryOperationExpression(
BinaryOperator.singleEquals, singleEqualsOperand, singleExpression);
singleEqualsOperand = null;
}
}
loop:
@ -1794,9 +1788,7 @@ relase. For details, see http://bit.ly/moz-document.
case $equal:
scanner.readChar();
if (singleEquals && scanner.peekChar() != $equal) {
resolveSpaceExpressions();
singleEqualsOperand = singleExpression;
singleExpression = null;
addOperator(BinaryOperator.singleEquals);
} else {
scanner.expectChar($equal);
addOperator(BinaryOperator.equals);
@ -2014,9 +2006,7 @@ relase. For details, see http://bit.ly/moz-document.
return ListExpression(commaExpressions, ListSeparator.comma,
brackets: bracketList,
span: bracketList ? scanner.spanFrom(beforeBracket) : null);
} else if (bracketList &&
spaceExpressions != null &&
singleEqualsOperand == null) {
} else if (bracketList && spaceExpressions != null) {
resolveOperations();
return ListExpression(
spaceExpressions..add(singleExpression), ListSeparator.space,

View File

@ -19,7 +19,9 @@ bool fuzzyEquals(num number1, num number2) =>
final _inverseEpsilon = 1 / epsilon;
/// Returns a hash code for [number] that matches [fuzzyEquals].
int fuzzyHashCode(num number) => (number * _inverseEpsilon).round().hashCode;
int fuzzyHashCode(num number) => number.isInfinite || number.isNaN
? number.hashCode
: (number * _inverseEpsilon).round().hashCode;
/// Returns whether [number1] is less than [number2], and not [fuzzyEquals].
bool fuzzyLessThan(num number1, num number2) =>

View File

@ -419,9 +419,9 @@ class _EvaluateVisitor
];
var metaMixins = [
AsyncBuiltInCallable.mixin("load-css", r"$module, $with: null",
AsyncBuiltInCallable.mixin("load-css", r"$url, $with: null",
(arguments) async {
var url = Uri.parse(arguments[0].assertString("module").text);
var url = Uri.parse(arguments[0].assertString("url").text);
var withMap = arguments[1].realNull?.assertMap("with")?.contents;
var configuration = const Configuration.empty();

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: 651a7e9f78b68bfd440241304301cf78711553a4
// Checksum: 485fce53ba9f381973c25a69b193a681891be098
//
// ignore_for_file: unused_import
@ -425,8 +425,8 @@ class _EvaluateVisitor
];
var metaMixins = [
BuiltInCallable.mixin("load-css", r"$module, $with: null", (arguments) {
var url = Uri.parse(arguments[0].assertString("module").text);
BuiltInCallable.mixin("load-css", r"$url, $with: null", (arguments) {
var url = Uri.parse(arguments[0].assertString("url").text);
var withMap = arguments[1].realNull?.assertMap("with")?.contents;
var configuration = const Configuration.empty();

View File

@ -1,5 +1,5 @@
name: sass
version: 1.28.0-dev
version: 1.28.0
description: A Sass implementation in Dart.
author: Sass Team
homepage: https://github.com/sass/dart-sass
@ -18,6 +18,7 @@ dependencies:
cli_repl: ">=0.1.3 <0.3.0"
collection: "^1.8.0"
meta: "^1.1.7"
node_interop: "^1.1.0"
js: "^0.6.0"
package_resolver: "^1.0.0"
path: "^1.6.0"
@ -32,8 +33,8 @@ dependencies:
dev_dependencies:
archive: ">=1.0.0 <3.0.0"
analyzer: ">=0.37.0 <0.40.0"
cli_pkg: "^1.0.0-beta.10"
analyzer: "^0.40.0"
cli_pkg: "^1.0.0-beta.12"
crypto: ">=0.9.2 <3.0.0"
dart_style: "^1.2.0"
grinder: "^0.8.0"
@ -46,9 +47,3 @@ dev_dependencies:
test_process: "^1.0.0-rc.1"
test: ">=0.12.42 <2.0.0"
yaml: "^2.0.0"
dependency_overrides:
cli_pkg:
git:
url: git://github.com/google/dart_cli_pkg
ref: 1.0.0-beta.10

View File

@ -9,7 +9,6 @@
import 'package:js/js.dart';
import 'package:path/path.dart' as p;
export 'package:sass/src/node/error.dart';
export 'package:sass/src/node/importer_result.dart';
export 'package:sass/src/node/render_context.dart';
export 'package:sass/src/node/render_options.dart';
@ -24,11 +23,6 @@ final sass = _requireSass(p.absolute("build/npm/sass.dart"));
/// The Fiber class.
final fiber = _requireFiber("fibers");
/// A `null` that's guaranteed to be represented by JavaScript's `undefined`
/// value, not by `null`.
@JS()
external Object get undefined;
/// A `null` that's guaranteed to be represented by JavaScript's `null` value,
/// not by `undefined`.
///
@ -39,9 +33,6 @@ final Object jsNull = _eval("null");
@JS("eval")
external Object _eval(String js);
@JS("process.chdir")
external void chdir(String directory);
@JS("require")
external Sass _requireSass(String path);

View File

@ -9,6 +9,7 @@ import 'dart:async';
import 'dart:js_util';
import 'package:js/js.dart';
import 'package:node_interop/js.dart';
import 'package:test/test.dart';
import '../ensure_npm_package.dart';
@ -236,7 +237,7 @@ void main() {
functions: jsify({
"foo": allowInterop((void done(Object result)) {
Timer(Duration.zero, () {
done(JSError("aw beans"));
done(JsError("aw beans"));
});
})
})));
@ -340,7 +341,7 @@ void main() {
functions: jsify({
"foo": allowInterop((void done(Object result)) {
Timer(Duration.zero, () {
done(JSError("aw beans"));
done(JsError("aw beans"));
});
})
}),

View File

@ -8,6 +8,8 @@
import 'dart:async';
import 'package:js/js.dart';
import 'package:node_interop/js.dart';
import 'package:node_interop/node.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
@ -275,10 +277,10 @@ void main() {
String oldWorkingDirectory;
setUp(() {
oldWorkingDirectory = currentPath;
chdir(sandbox);
process.chdir(sandbox);
});
tearDown(() => chdir(oldWorkingDirectory));
tearDown(() => process.chdir(oldWorkingDirectory));
test("is resolved relative to the CWD", () {
expect(
@ -575,7 +577,7 @@ void main() {
test("an error is returned", () {
var error = renderSyncError(RenderOptions(
data: "@import 'foo'",
importer: allowInterop((void _, void __) => JSError("oh no"))));
importer: allowInterop((void _, void __) => JsError("oh no"))));
expect(
error,
@ -654,7 +656,7 @@ void main() {
importer:
allowInterop((void _, void __, void done(Object result)) {
Timer(Duration.zero, () {
done(JSError('oh no'));
done(JsError('oh no'));
});
}))),
completion(toStringAndMessageEqual("oh no\n"
@ -742,7 +744,7 @@ void main() {
importer:
allowInterop((void _, void __, void done(Object result)) {
Timer(Duration.zero, () {
done(JSError('oh no'));
done(JsError('oh no'));
});
}),
fiber: fiber)),

View File

@ -8,6 +8,7 @@ import 'dart:js_util';
import 'package:js/js.dart';
import 'package:test/test.dart';
import 'package:node_interop/node_interop.dart';
import 'package:sass/src/io.dart';
import 'package:sass/src/node/function.dart';
@ -97,8 +98,8 @@ void runTestInSandbox() {
sass;
var oldWorkingDirectory = currentPath;
chdir(sandbox);
addTearDown(() => chdir(oldWorkingDirectory));
process.chdir(sandbox);
addTearDown(() => process.chdir(oldWorkingDirectory));
}
/// Sets the environment variable [name] to [value] within this process.

View File

@ -26,11 +26,7 @@ void main(List<String> args) {
pkg.chocolateyNuspec.value = _nuspec;
pkg.homebrewRepo.value = "sass/homebrew-sass";
pkg.homebrewFormula.value = "sass.rb";
pkg.jsRequires.value = {
"fs": "fs",
"chokidar": "chokidar",
"readline": "readline"
};
pkg.jsRequires.value = {"chokidar": "chokidar", "readline": "readline"};
pkg.jsModuleMainLibrary.value = "lib/src/node.dart";
pkg.npmPackageJson.fn = () =>
json.decode(File("package/package.json").readAsStringSync())

View File

@ -43,7 +43,8 @@ final _sharedClasses = const ['EvaluateResult', 'CompileResult'];
void synchronize() {
sources.forEach((source, target) {
var visitor = _Visitor(File(source).readAsStringSync(), source);
parseFile(path: source, featureSet: FeatureSet.fromEnableFlags([]))
parseFile(path: source, featureSet: FeatureSet.latestLanguageVersion())
.unit
.accept(visitor);
var formatted = DartFormatter().format(visitor.result);