Skip to content

Commit d9d3092

Browse files
authored
Merge pull request #2256 from sass/merge-main
Merge origin/main into feature.color-4
2 parents 6876968 + a59af7a commit d9d3092

40 files changed

+1197
-232
lines changed

.pubignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This should be identical to .gitignore except that it doesn't exclude
2-
# generated protobuf files.
2+
# generated Dart files.
33

44
.buildlog
55
.DS_Store

CHANGELOG.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 1.76.0
1+
## 1.78.0
22

33
* **Breaking change**: Passing a number with unit `%` to the `$alpha` parameter
44
of `color.change()`, `color.adjust()`, `change-color()`, and `adjust-color()`
@@ -184,6 +184,44 @@
184184

185185
* Remove `RgbColor`, `HslColor` and `HwbColor` SassScript values.
186186

187+
## 1.77.3
188+
189+
### Dart API
190+
191+
* `Deprecation.duplicateVariableFlags` has been deprecated and replaced with
192+
`Deprecation.duplicateVarFlags` to make it consistent with the
193+
`duplicate-var-flags` name used on the command line and in the JS API.
194+
195+
## 1.77.2
196+
197+
* Don't emit deprecation warnings for functions and mixins beginning with `__`.
198+
199+
* Allow user-defined functions whose names begin with `_` and otherwise look
200+
like vendor-prefixed functions with special CSS syntax.
201+
202+
### Command-Line Interface
203+
204+
* Properly handle the `--silence-deprecation` flag.
205+
206+
* Handle the `--fatal-deprecation` and `--future-deprecation` flags for
207+
`--interactive` mode.
208+
209+
## 1.77.1
210+
211+
* Fix a crash that could come up with importers in certain contexts.
212+
213+
## 1.77.0
214+
215+
* *Don't* throw errors for at-rules in keyframe blocks.
216+
217+
## 1.76.0
218+
219+
* Throw errors for misplaced statements in keyframe blocks.
220+
221+
* Mixins and functions whose names begin with `--` are now deprecated for
222+
forwards-compatibility with the in-progress CSS functions and mixins spec.
223+
This deprecation is named `css-function-mixin`.
224+
187225
## 1.75.0
188226

189227
* Fix a bug in which stylesheet canonicalization could be cached incorrectly

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ A [Dart][dart] implementation of [Sass][sass]. **Sass makes CSS fun**.
4242
* [Compatibility Policy](#compatibility-policy)
4343
* [Browser Compatibility](#browser-compatibility)
4444
* [Node.js Compatibility](#nodejs-compatibility)
45+
* [Invalid CSS](#invalid-css)
4546
* [Embedded Dart Sass](#embedded-dart-sass)
4647
* [Usage](#usage)
4748
* [Behavioral Differences from Ruby Sass](#behavioral-differences-from-ruby-sass)
@@ -405,6 +406,18 @@ considers itself free to break support if necessary.
405406

406407
[the Node.js release page]: https://nodejs.org/en/about/previous-releases
407408

409+
### Invalid CSS
410+
411+
Changes to the behavior of Sass stylesheets that produce invalid CSS output are
412+
_not_ considered breaking changes. Such changes are almost always necessary when
413+
adding support for new CSS features, and delaying all such features until a new
414+
major version would be unduly burdensome for most users.
415+
416+
For example, when Sass began parsing `calc()` expressions, the invalid
417+
expression `calc(1 +)` became a Sass error where before it was passed through
418+
as-is. This was not considered a breaking change, because `calc(1 +)` was never
419+
valid CSS to begin with.
420+
408421
## Embedded Dart Sass
409422

410423
Dart Sass includes an implementation of the compiler side of the [Embedded Sass

lib/src/ast/sass/expression/function.dart

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ final class FunctionExpression
2323
/// without a namespace.
2424
final String? namespace;
2525

26+
/// The name of the function being invoked, with underscores converted to
27+
/// hyphens.
28+
///
29+
/// If this function is a plain CSS function, use [originalName] instead.
30+
final String name;
31+
2632
/// The name of the function being invoked, with underscores left as-is.
2733
final String originalName;
2834

@@ -31,12 +37,6 @@ final class FunctionExpression
3137

3238
final FileSpan span;
3339

34-
/// The name of the function being invoked, with underscores converted to
35-
/// hyphens.
36-
///
37-
/// If this function is a plain CSS function, use [originalName] instead.
38-
String get name => originalName.replaceAll('_', '-');
39-
4040
FileSpan get nameSpan {
4141
if (namespace == null) return span.initialIdentifier();
4242
return span.withoutNamespace().initialIdentifier();
@@ -46,7 +46,8 @@ final class FunctionExpression
4646
namespace == null ? null : span.initialIdentifier();
4747

4848
FunctionExpression(this.originalName, this.arguments, this.span,
49-
{this.namespace});
49+
{this.namespace})
50+
: name = originalName.replaceAll('_', '-');
5051

5152
T accept<T>(ExpressionVisitor<T> visitor) =>
5253
visitor.visitFunctionExpression(this);

lib/src/ast/sass/statement/callable_declaration.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ abstract base class CallableDeclaration
1818
/// The name of this callable, with underscores converted to hyphens.
1919
final String name;
2020

21+
/// The callable's original name, without underscores converted to hyphens.
22+
final String originalName;
23+
2124
/// The comment immediately preceding this declaration.
2225
final SilentComment? comment;
2326

@@ -26,8 +29,9 @@ abstract base class CallableDeclaration
2629

2730
final FileSpan span;
2831

29-
CallableDeclaration(
30-
this.name, this.arguments, Iterable<Statement> children, this.span,
32+
CallableDeclaration(this.originalName, this.arguments,
33+
Iterable<Statement> children, this.span,
3134
{this.comment})
32-
: super(List.unmodifiable(children));
35+
: name = originalName.replaceAll('_', '-'),
36+
super(List.unmodifiable(children));
3337
}

lib/src/ast/sass/statement/include_rule.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ final class IncludeRule
2525
/// hyphens.
2626
final String name;
2727

28+
/// The original name of the mixin being invoked, without underscores
29+
/// converted to hyphens.
30+
final String originalName;
31+
2832
/// The arguments to pass to the mixin.
2933
final ArgumentInvocation arguments;
3034

@@ -55,8 +59,9 @@ final class IncludeRule
5559
return startSpan.initialIdentifier();
5660
}
5761

58-
IncludeRule(this.name, this.arguments, this.span,
59-
{this.namespace, this.content});
62+
IncludeRule(this.originalName, this.arguments, this.span,
63+
{this.namespace, this.content})
64+
: name = originalName.replaceAll('_', '-');
6065

6166
T accept<T>(StatementVisitor<T> visitor) => visitor.visitIncludeRule(this);
6267

lib/src/async_import_cache.dart

Lines changed: 67 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import 'package:path/path.dart' as p;
1111
import 'ast/sass.dart';
1212
import 'deprecation.dart';
1313
import 'importer.dart';
14+
import 'importer/canonicalize_context.dart';
1415
import 'importer/no_op.dart';
1516
import 'importer/utils.dart';
1617
import 'io.dart';
1718
import 'logger.dart';
19+
import 'util/map.dart';
1820
import 'util/nullable.dart';
1921
import 'utils.dart';
2022

@@ -43,30 +45,28 @@ final class AsyncImportCache {
4345
/// The `forImport` in each key is true when this canonicalization is for an
4446
/// `@import` rule. Otherwise, it's for a `@use` or `@forward` rule.
4547
///
46-
/// This cache isn't used for relative imports, because they depend on the
47-
/// specific base importer. That's stored separately in
48-
/// [_relativeCanonicalizeCache].
48+
/// This cache covers loads that go through the entire chain of [_importers],
49+
/// but it doesn't cover individual loads or loads in which any importer
50+
/// accesses `containingUrl`. See also [_perImporterCanonicalizeCache].
4951
final _canonicalizeCache =
5052
<(Uri, {bool forImport}), AsyncCanonicalizeResult?>{};
5153

52-
/// The canonicalized URLs for each non-canonical URL that's resolved using a
53-
/// relative importer.
54+
/// Like [_canonicalizeCache] but also includes the specific importer in the
55+
/// key.
5456
///
55-
/// The map's keys have four parts:
57+
/// This is used to cache both relative imports from the base importer and
58+
/// individual importer results in the case where some other component of the
59+
/// importer chain isn't cacheable.
60+
final _perImporterCanonicalizeCache =
61+
<(AsyncImporter, Uri, {bool forImport}), AsyncCanonicalizeResult?>{};
62+
63+
/// A map from the keys in [_perImporterCanonicalizeCache] that are generated
64+
/// for relative URL loads agains the base importer to the original relative
65+
/// URLs what were loaded.
5666
///
57-
/// 1. The URL passed to [canonicalize] (the same as in [_canonicalizeCache]).
58-
/// 2. Whether the canonicalization is for an `@import` rule.
59-
/// 3. The `baseImporter` passed to [canonicalize].
60-
/// 4. The `baseUrl` passed to [canonicalize].
61-
///
62-
/// The map's values are the same as the return value of [canonicalize].
63-
final _relativeCanonicalizeCache = <(
64-
Uri, {
65-
bool forImport,
66-
AsyncImporter baseImporter,
67-
Uri? baseUrl
68-
}),
69-
AsyncCanonicalizeResult?>{};
67+
/// This is used to invalidate the cache when files are changed.
68+
final _nonCanonicalRelativeUrls =
69+
<(AsyncImporter, Uri, {bool forImport}), Uri>{};
7070

7171
/// The parsed stylesheets for each canonicalized import URL.
7272
final _importCache = <Uri, Stylesheet?>{};
@@ -154,18 +154,17 @@ final class AsyncImportCache {
154154
}
155155

156156
if (baseImporter != null && url.scheme == '') {
157-
var relativeResult = await putIfAbsentAsync(_relativeCanonicalizeCache, (
158-
url,
159-
forImport: forImport,
160-
baseImporter: baseImporter,
161-
baseUrl: baseUrl
162-
), () async {
163-
var (result, cacheable) = await _canonicalize(
164-
baseImporter, baseUrl?.resolveUri(url) ?? url, baseUrl, forImport);
157+
var resolvedUrl = baseUrl?.resolveUri(url) ?? url;
158+
var key = (baseImporter, resolvedUrl, forImport: forImport);
159+
var relativeResult =
160+
await putIfAbsentAsync(_perImporterCanonicalizeCache, key, () async {
161+
var (result, cacheable) =
162+
await _canonicalize(baseImporter, resolvedUrl, baseUrl, forImport);
165163
assert(
166164
cacheable,
167165
"Relative loads should always be cacheable because they never "
168166
"provide access to the containing URL.");
167+
if (baseUrl != null) _nonCanonicalRelativeUrls[key] = url;
169168
return result;
170169
});
171170
if (relativeResult != null) return relativeResult;
@@ -181,17 +180,41 @@ final class AsyncImportCache {
181180
// `canonicalize()` calls we've attempted are cacheable. Only if they are do
182181
// we store the result in the cache.
183182
var cacheable = true;
184-
for (var importer in _importers) {
183+
for (var i = 0; i < _importers.length; i++) {
184+
var importer = _importers[i];
185+
var perImporterKey = (importer, url, forImport: forImport);
186+
switch (_perImporterCanonicalizeCache.getOption(perImporterKey)) {
187+
case (var result?,):
188+
return result;
189+
case (null,):
190+
continue;
191+
}
192+
185193
switch (await _canonicalize(importer, url, baseUrl, forImport)) {
186194
case (var result?, true) when cacheable:
187195
_canonicalizeCache[key] = result;
188196
return result;
189197

190-
case (var result?, _):
191-
return result;
192-
193-
case (_, false):
194-
cacheable = false;
198+
case (var result, true) when !cacheable:
199+
_perImporterCanonicalizeCache[perImporterKey] = result;
200+
if (result != null) return result;
201+
202+
case (var result, false):
203+
if (cacheable) {
204+
// If this is the first uncacheable result, add all previous results
205+
// to the per-importer cache so we don't have to re-run them for
206+
// future uses of this importer.
207+
for (var j = 0; j < i; j++) {
208+
_perImporterCanonicalizeCache[(
209+
_importers[j],
210+
url,
211+
forImport: forImport
212+
)] = null;
213+
}
214+
cacheable = false;
215+
}
216+
217+
if (result != null) return result;
195218
}
196219
}
197220

@@ -206,18 +229,17 @@ final class AsyncImportCache {
206229
/// that result is cacheable at all.
207230
Future<(AsyncCanonicalizeResult?, bool cacheable)> _canonicalize(
208231
AsyncImporter importer, Uri url, Uri? baseUrl, bool forImport) async {
209-
var canonicalize = forImport
210-
? () => inImportRule(() => importer.canonicalize(url))
211-
: () => importer.canonicalize(url);
212-
213232
var passContainingUrl = baseUrl != null &&
214233
(url.scheme == '' || await importer.isNonCanonicalScheme(url.scheme));
215-
var result = await withContainingUrl(
216-
passContainingUrl ? baseUrl : null, canonicalize);
217234

218-
// TODO(sass/dart-sass#2208): Determine whether the containing URL was
219-
// _actually_ accessed rather than assuming it was.
220-
var cacheable = !passContainingUrl || importer is FilesystemImporter;
235+
var canonicalizeContext =
236+
CanonicalizeContext(passContainingUrl ? baseUrl : null, forImport);
237+
238+
var result = await withCanonicalizeContext(
239+
canonicalizeContext, () => importer.canonicalize(url));
240+
241+
var cacheable =
242+
!passContainingUrl || !canonicalizeContext.wasContainingUrlAccessed;
221243

222244
if (result == null) return (null, cacheable);
223245

@@ -315,7 +337,7 @@ final class AsyncImportCache {
315337
Uri sourceMapUrl(Uri canonicalUrl) =>
316338
_resultsCache[canonicalUrl]?.sourceMapUrl ?? canonicalUrl;
317339

318-
/// Clears the cached canonical version of the given [url].
340+
/// Clears the cached canonical version of the given non-canonical [url].
319341
///
320342
/// Has no effect if the canonical version of [url] has not been cached.
321343
///
@@ -324,7 +346,8 @@ final class AsyncImportCache {
324346
void clearCanonicalize(Uri url) {
325347
_canonicalizeCache.remove((url, forImport: false));
326348
_canonicalizeCache.remove((url, forImport: true));
327-
_relativeCanonicalizeCache.removeWhere((key, _) => key.$1 == url);
349+
_perImporterCanonicalizeCache.removeWhere(
350+
(key, _) => key.$2 == url || _nonCanonicalRelativeUrls[key] == url);
328351
}
329352

330353
/// Clears the cached parse tree for the stylesheet with the given

0 commit comments

Comments
 (0)