Skip to content

Commit

Permalink
Merge pull request #830 from sass/merge-master
Browse files Browse the repository at this point in the history
Merge master into feature.use
  • Loading branch information
nex3 committed Sep 26, 2019
2 parents cfd5cd7 + 93a3a6f commit 7bfbba0
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 44 deletions.
39 changes: 35 additions & 4 deletions .travis.yml
Expand Up @@ -64,6 +64,9 @@ jobs:
env: DART_CHANNEL=dev
- <<: *dart-tests
os: windows
# File system watching is extra flaky on Dart 2.5.0 on Windows (see
# dart-lang/sdk#38334).
env: DART_VERSION=2.4.1
- <<: *dart-tests
os: osx

Expand Down Expand Up @@ -110,10 +113,12 @@ jobs:
(type IN (push, api)) AND (repo = sass/dart-sass) AND tag =~ ^\d+\.\d+\.\d+([+-].*)?$
script: pub run grinder sanity-check-before-release

# Deploy Linux and Windows releases to GitHub. Mac OS releases are deployed in
# a later stage so that we can build application snapshots on Mac OS bots.
# Deploy Linux releases to GitHub. Mac OS releases are deployed in a later
# stage so that we can build application snapshots on Mac OS bots, and Windows
# releases are deployed later so they can use Dart 2.4.1 to work around
# dart-lang/sdk#38334.
- stage: deploy 1
name: "GitHub: Windows and Linux"
name: "GitHub: Linux"
if: *deploy-if
env: &github-env
- GITHUB_USER=sassbot
Expand All @@ -125,7 +130,7 @@ jobs:
script: skip # Don't run tests
deploy:
provider: script
script: pub run grinder github-release github-linux github-windows
script: pub run grinder github-release github-linux
skip_cleanup: true # Don't clean up the Dart SDK.

# This causes the deploy to only be build when a tag is pushed. This
Expand Down Expand Up @@ -183,6 +188,9 @@ jobs:
- name: Chocolatey
if: *deploy-if
env:
# File system watching is extra flaky on Dart 2.5.0 on Windows (see
# dart-lang/sdk#38334).
- DART_VERSION=2.4.1
# CHOCO_TOKEN="..."
- secure: "cW11kQYBBEElfVsc1pJfVEHOMYwt0ZK+9STZHwSPbAISlplIRnsimMN7TqCY2aLnkWXyUMU7DphIl9uQ86M4BT1bJopsHbapj27bFSlKWHlBSDB/xylFHywV41Yk5lMlr8DLMbsSzVahasyR34xS6HYIRlDpZ9TFiQuDQNJxQmqTZJg/FC+3nqCI7tyMKGkWc48ikTcmqDMHsG9CudG2u+Q3S9sLNXArh9T4tSnAyWkTvSrS05mvFx5tC83PcG9/VkioTId+VRSJchwTmCxDFDROrTikTXZMtYn8wMAQ2wQ34TQXNZMZ9uiHA6W0IuJV2EnYerJbqV2lrJq9xqZywKu6HW6i4GhrCvizALNFZx/N7s/10xuf3UcuWizYml/e0MYT+6t4ojTYBMKv+Cx+H2Y2Jdpvdn2ZAIl6LaU3pLw4OIPJ7aXjDwZd63MPxtwGwVLHbH7Zu+oUv1erIq5LtatuocGWipD8WdiMBQvyCuDRMowpLPoAbj+mevOf+xlY2Eym4tOXpxM7iY3lXFHROo5dQbhsARfVF9J1gl5PuYXvCjxqTfK/ef9t3ZoDbi57+yAJUWlZfWa5r1zKE8OS0pA8GfQRLom/Lt0wKVw4Xiofgolzd9pEHi4JpsYIQb8O+u1ACQU6nBCS87CGrQ+ylnzKfGUs0aW2K3gvbkg0LUg="
script: skip
Expand Down Expand Up @@ -232,3 +240,26 @@ jobs:
script: pub run grinder github-mac-os
skip_cleanup: true
on: {tags: true}

- name: "GitHub: Windows"
if: *deploy-if
env:
# We can't re-use the github-env alias here because we also need to
# override DART_VERSION.
#
# File system watching is extra flaky on Dart 2.5.0 on Windows (see
# dart-lang/sdk#38334).
- DART_VERSION=2.4.1
- GITHUB_USER=sassbot
# GITHUB_AUTH="..."
#
# Note that this overrides the read-only auth token that's set for all
# builds.
- secure: "AAP74aT+8SQmwGeHrCsZ7GgppvCCkDAZXszivocMy3Fi9gfMCLABBCh67pGINJX4VlLW7ftPF3xivlvgGu+e4ncXz9m9jIPZ9Iza3cW5jCnCgyRGZD98gwabIDFWiv4X9V2xnJA2p1ZuYBf8Sh3TTipUFBKMjlnxVxYkIOTud4rUss/htFhxVA/oFTo0ThTZwXuxJ+GRGTM4PcuHPJvPf18iRPs2AHFV6ZP51xgc3AsXC6Zyom5EJeX0yGj9zWQ0XCjnuFdGsI6G9jmkrmqgAXuUipgqAn0tjxPYp9R/1HqnBLD3Zbrvyi5pCiSFclU6CS6kTDbefzPOc5+zrnlkaolVeF8tQ+EhZiZqtLnpLYUz9bgknoFUapUN4N0R36sKBStdRv54+sMeoOzpQ8ep3PeZW5nWbak12wcrDx38ToWs6hQ4ycb0SQDZZatHsASpSu2nX8HwzZSDAZmsAdB+epPmgA0CBjWVG1ycmVnT6l3OopUmbaY3pXBNzFUXq5Fcd7Q39/MfrmHpyxSc3QVf8xNtUx9ggYtK0Kwx6dgykhNMVzFGZRVyQgwpaiyDqgMGEU2GQzzcJhgKo9+y1fDtdfj/cctmvJ2Fo1fkk+DMkEPUHGOVo6uKFnartky9iLm1WiHDMruJ6SIOJzAnb+TMBWQTSwI+F4wyEiRVR8Zv4uA="

script: skip
deploy:
provider: script
script: pub run grinder github-windows
skip_cleanup: true
on: {tags: true}
23 changes: 23 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,26 @@
## 1.22.12

* **Potentially breaking bug fix:** character sequences consisting of two or
more hyphens followed by a number (such as `--123`), or two or more hyphens on
their own (such as `--`), are now parsed as identifiers [in accordance with
the CSS spec][ident-token-diagram].

[ident-token-diagram]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram

The sequence `--` was previously parsed as multiple applications of the `-`
operator. Since this is unlikely to be used intentionally in practice, we
consider this bug fix safe.

### Command-Line Interface

* Fix a bug where changes in `.css` files would be ignored in `--watch` mode.

### JavaScript API

* Allow underscore-separated custom functions to be defined.

* Improve the performance of Node.js compilation involving many `@import`s.

## 1.22.11

* Don't try to load unquoted plain-CSS indented-syntax imports.
Expand Down
4 changes: 3 additions & 1 deletion lib/src/executable/watch.dart
Expand Up @@ -122,7 +122,9 @@ class _Watcher {
Future<void> watch(MultiDirWatcher watcher) async {
await for (var event in _debounceEvents(watcher.events)) {
var extension = p.extension(event.path);
if (extension != '.sass' && extension != '.scss') continue;
if (extension != '.sass' && extension != '.scss' && extension != '.css') {
continue;
}

switch (event.type) {
case ChangeType.MODIFY:
Expand Down
44 changes: 30 additions & 14 deletions lib/src/io/node.dart
Expand Up @@ -156,23 +156,39 @@ String _cleanErrorMessage(_SystemError error) {
}

bool fileExists(String path) {
try {
return _fs.statSync(path).isFile();
} catch (error) {
var systemError = error as _SystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
return _systemErrorToFileSystemException(() {
// `existsSync()` is faster than `statSync()`, but it doesn't clarify
// 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;

try {
return _fs.statSync(path).isFile();
} catch (error) {
var systemError = error as _SystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
});
}

bool dirExists(String path) {
try {
return _fs.statSync(path).isDirectory();
} catch (error) {
var systemError = error as _SystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
return _systemErrorToFileSystemException(() {
// `existsSync()` is faster than `statSync()`, but it doesn't clarify
// 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;

try {
return _fs.statSync(path).isDirectory();
} catch (error) {
var systemError = error as _SystemError;
if (systemError.code == 'ENOENT') return false;
rethrow;
}
});
}

void ensureDir(String path) {
Expand Down
18 changes: 10 additions & 8 deletions lib/src/parse/parser.dart
Expand Up @@ -147,12 +147,18 @@ class Parser {
@protected
String identifier({bool normalize = false, bool unit = false}) {
// NOTE: this logic is largely duplicated in
// StylesheetParser._interpolatedIdentifier and isIdentifier in utils.dart.
// Most changes here should be mirrored there.
// StylesheetParser.interpolatedIdentifier. Most changes here should be
// mirrored there.

var text = StringBuffer();
while (scanner.scanChar($dash)) {
if (scanner.scanChar($dash)) {
text.writeCharCode($dash);

if (scanner.scanChar($dash)) {
text.writeCharCode($dash);
_identifierBody(text, normalize: normalize, unit: unit);
return text.toString();
}
}

var first = scanner.peekChar();
Expand Down Expand Up @@ -580,11 +586,7 @@ class Parser {

var second = scanner.peekChar(forward + 1);
if (second == null) return false;
if (isNameStart(second) || second == $backslash) return true;
if (second != $dash) return false;

var third = scanner.peekChar(forward + 2);
return third != null && isNameStart(third);
return isNameStart(second) || second == $backslash || second == $dash;
}

/// Returns whether the scanner is immediately before a sequence of characters
Expand Down
26 changes: 15 additions & 11 deletions lib/src/parse/stylesheet.dart
Expand Up @@ -3102,8 +3102,14 @@ relase. For details, see http://bit.ly/moz-document.
var start = scanner.state;
var buffer = InterpolationBuffer();

while (scanner.scanChar($dash)) {
if (scanner.scanChar($dash)) {
buffer.writeCharCode($dash);

if (scanner.scanChar($dash)) {
buffer.writeCharCode($dash);
_interpolatedIdentifierBody(buffer);
return buffer.interpolation(scanner.spanFrom(start));
}
}

var first = scanner.peekChar();
Expand All @@ -3119,6 +3125,13 @@ relase. For details, see http://bit.ly/moz-document.
scanner.error("Expected identifier.");
}

_interpolatedIdentifierBody(buffer);
return buffer.interpolation(scanner.spanFrom(start));
}

/// Consumes a chunk of a possibly-interpolated CSS identifier after the name
/// start, and adds the contents to the [buffer] buffer.
void _interpolatedIdentifierBody(InterpolationBuffer buffer) {
while (true) {
var next = scanner.peekChar();
if (next == null) {
Expand All @@ -3136,8 +3149,6 @@ relase. For details, see http://bit.ly/moz-document.
break;
}
}

return buffer.interpolation(scanner.spanFrom(start));
}

/// Consumes interpolation.
Expand Down Expand Up @@ -3384,15 +3395,8 @@ relase. For details, see http://bit.ly/moz-document.
if (first != $dash) return false;
var second = scanner.peekChar(1);
if (second == null) return false;
if (isNameStart(second) || second == $backslash) return true;

if (second == $hash) return scanner.peekChar(2) == $lbrace;
if (second != $dash) return false;

var third = scanner.peekChar(2);
if (third == null) return false;
if (third == $hash) return scanner.peekChar(3) == $lbrace;
return isNameStart(third);
return isNameStart(second) || second == $backslash || second == $dash;
}

/// Returns whether the scanner is immediately before a sequence of characters
Expand Down
2 changes: 1 addition & 1 deletion lib/src/visitor/async_evaluate.dart
Expand Up @@ -442,7 +442,7 @@ class _EvaluateVisitor

functions = [...?functions, ...globalFunctions, ...metaFunctions];
for (var function in functions) {
_builtInFunctions[function.name] = function;
_builtInFunctions[function.name.replaceAll("_", "-")] = function;
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/visitor/evaluate.dart
Expand Up @@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: 5c9f270ef574f9c6062421ed1866af3d07672b46
// Checksum: 3fc19891432af3cebdc0f36730e57cbbf672d959
//
// ignore_for_file: unused_import

Expand Down Expand Up @@ -448,7 +448,7 @@ class _EvaluateVisitor

functions = [...?functions, ...globalFunctions, ...metaFunctions];
for (var function in functions) {
_builtInFunctions[function.name] = function;
_builtInFunctions[function.name.replaceAll("_", "-")] = function;
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/src/visitor/recursive_statement.dart
Expand Up @@ -23,7 +23,7 @@ import 'interface/statement.dart';
/// The default implementation of the visit methods all return `null`.
abstract class RecursiveStatementVisitor<T> implements StatementVisitor<T> {
T visitAtRootRule(AtRootRule node) {
visitInterpolation(node.query);
if (node.query != null) visitInterpolation(node.query);
return visitChildren(node);
}

Expand All @@ -47,7 +47,7 @@ abstract class RecursiveStatementVisitor<T> implements StatementVisitor<T> {

T visitDeclaration(Declaration node) {
visitInterpolation(node.name);
visitExpression(node.value);
if (node.value != null) visitExpression(node.value);
return node.children == null ? null : visitChildren(node);
}

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
@@ -1,5 +1,5 @@
name: sass
version: 1.22.11
version: 1.22.12
description: A Sass implementation in Dart.
author: Sass Team
homepage: https://github.com/sass/dart-sass
Expand Down
4 changes: 4 additions & 0 deletions test/cli/dart/watch_test.dart
Expand Up @@ -5,6 +5,10 @@
// OS X's modification time reporting is flaky, so we skip these tests on it.
@TestOn('vm && !mac-os')

// File watching is inherently flaky at the OS level. To mitigate this, we do a
// few retries when the tests fail.
@Retry(3)

import 'package:test/test.dart';

import '../dart_test.dart';
Expand Down
4 changes: 4 additions & 0 deletions test/cli/node/watch_test.dart
Expand Up @@ -6,6 +6,10 @@
@TestOn('vm && !mac-os')
@Tags(['node'])

// File watching is inherently flaky at the OS level. To mitigate this, we do a
// few retries when the tests fail.
@Retry(3)

import 'package:test/test.dart';

import '../../ensure_npm_package.dart';
Expand Down
20 changes: 20 additions & 0 deletions test/cli/shared/watch.dart
Expand Up @@ -499,6 +499,26 @@ void sharedTests(Future<TestProcess> runSass(Iterable<String> arguments)) {
]).validate();
});
});

// Regression test for #806
test("with a .css extension", () async {
await d.file("test.css", "a {b: c}").create();

var sass = await watch(["test.css:out.css"]);
await expectLater(
sass.stdout, emits('Compiled test.css to out.css.'));
await expectLater(sass.stdout, _watchingForChanges);
await tickIfPoll();

await d.file("test.css", "x {y: z}").create();
await expectLater(
sass.stdout, emits('Compiled test.css to out.css.'));
await sass.kill();

await d
.file("out.css", equalsIgnoringWhitespace("x { y: z; }"))
.validate();
});
});

group("doesn't recompile the watched file", () {
Expand Down
24 changes: 24 additions & 0 deletions test/dart_api/function_test.dart
Expand Up @@ -130,4 +130,28 @@ main() {

expect(css, equalsIgnoringWhitespace("a { b: 1; }"));
});

group("are dash-normalized", () {
test("when defined with dashes", () {
expect(
compileString('a {b: foo_bar()}', functions: [
Callable("foo-bar", "", expectAsync1((arguments) {
expect(arguments, isEmpty);
return sassNull;
}))
]),
isEmpty);
});

test("when defined with underscores", () {
expect(
compileString('a {b: foo-bar()}', functions: [
Callable("foo_bar", "", expectAsync1((arguments) {
expect(arguments, isEmpty);
return sassNull;
}))
]),
isEmpty);
});
});
}

0 comments on commit 7bfbba0

Please sign in to comment.