Merge pull request #3877 from sass/merge-main

Merge origin/main into feature.color-4
This commit is contained in:
Natalie Weizenbaum 2024-05-30 16:00:30 -07:00 committed by GitHub
commit 6690da3822
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 883 additions and 81 deletions

View File

@ -72,7 +72,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: bufbuild/buf-setup-action@v1.30.0
- uses: bufbuild/buf-setup-action@v1.32.1
with: {github_token: "${{ github.token }}"}
- name: Generate protobuf code
run: buf generate
@ -138,7 +138,7 @@ jobs:
- name: Find changed files in js-api-doc
id: changed-files
uses: tj-actions/changed-files@2d756ea4c53f7f6b397767d8723b3a10a9f35bf2
uses: tj-actions/changed-files@03334d095e2739fa9ac4034ec16f66d5d01e9eba
with: {files: js-api-doc}
- name: Deploy

View File

@ -4,6 +4,11 @@
* Remove `RgbColor`, `HslColor` and `HwbColor` SassScript values.
## 2.7.0
* Add `containing_url_unused` field to `CanonicalizeResponse` and
`FileImportResponse`.
## 2.6.0
* Add `fatal_deprecation`, `silence_deprecation`, and `future_deprecation`

View File

@ -0,0 +1,67 @@
# Preparing for Plain CSS Functions and Mixins: Draft 1
*([Issue](https://github.com/sass/sass/issues/3787))*
This proposal adds errors for existing Sass syntax which is likely to conflict
with upcoming plain CSS support for functions and mixins.
## Table of Contents
* [Background](#background)
* [Summary](#summary)
* [Semantics](#semantics)
* [`@mixin`](#mixin)
* [`@function`](#function)
* [Deprecation Process](#deprecation-process)
## Background
> This section is non-normative.
In [this informal proposal] and [this issue], the CSS working group have begun
seriously discussing the possibility of introducing functions and mixins to CSS
itself. Although many specific details are still in flux, all syntaxes that have
been floated have two things in common:
[this informal proposal]: https://css.oddbird.net/sasslike/mixins-functions/
[this issue]: https://github.com/w3c/csswg-drafts/issues/9350
* They use at-rules named `@mixin` and `@function`, just as Sass does today.
* They require custom identifiers beginning with `--` after these at-rules.
Sass currently allows functions and mixins whose names begin with `--`, but this
is a relatively painless thing to deprecate and would allow us full CSS
compatibility with whatever version of the proposal ends up getting implemented.
## Summary
> This section is non-normative.
This proposal makes any mixin or function whose name begins with `--` an error.
This will be preceded by a deprecation period in which those mixins and
functions will just produce warnings.
## Semantics
### `@mixin`
Update [the `@mixin` semantics] to add after the first line:
[the `@mixin` semantics]: ../spec/at-rules/mixin.md
* If `name` begins with `--`, throw an error.
### `@function`
Update [the `@function` semantics] to add after the first line:
[the `@function` semantics]: ../spec/at-rules/function.md
* If `name` begins with `--`, throw an error.
## Deprecation Process
During the deprecation period, instead of throwing errors as described in [the
semantics section] above, emit a deprecation warning named `css-function-mixin`.
[the semantics section]: #semantics

View File

@ -5,22 +5,25 @@
* `fatalDeprecations`, `futureDeprecations`, or `silenceDeprecations`.
*/
export interface Deprecations {
// START AUTOGENERATED LIST
// Checksum: eb5e3889156f5ed12e119f11786f06f91a5f05c2
/**
* Deprecation for passing a string to `call` instead of using `get-function`.
* Deprecation for passing a string directly to meta.call().
*
* This deprecation has been active in all versions of Dart Sass.
* This deprecation was active in the first version of Dart Sass.
*/
'call-string': Deprecation<'call-string'>;
/**
* Deprecation for `@elseif`.
* Deprecation for @elseif.
*
* This deprecation became active in Dart Sass 1.3.2.
*/
elseif: Deprecation<'elseif'>;
/**
* Deprecation for parsing `@-moz-document`.
* Deprecation for @-moz-document.
*
* This deprecation became active in Dart Sass 1.7.2.
*/
@ -29,27 +32,26 @@ export interface Deprecations {
/**
* Deprecation for imports using relative canonical URLs.
*
* This deprecation became active in Dart Sass 1.17.2.
* This deprecation became active in Dart Sass 1.14.2.
*/
'relative-canonical': Deprecation<'relative-canonical'>;
/**
* Deprecation for declaring new variables with `!global`.
* Deprecation for declaring new variables with !global.
*
* This deprecation became active in Dart Sass 1.17.2.
*/
'new-global': Deprecation<'new-global'>;
/**
* Deprecation for using color module functions in place of plain CSS
* functions.
* Deprecation for using color module functions in place of plain CSS functions.
*
* This deprecation became active in Dart Sass 1.23.0.
*/
'color-module-compat': Deprecation<'color-module-compat'>;
/**
* Deprecation for treating `/` as division.
* Deprecation for / operator for division.
*
* This deprecation became active in Dart Sass 1.33.0.
*/
@ -63,22 +65,21 @@ export interface Deprecations {
'bogus-combinators': Deprecation<'bogus-combinators'>;
/**
* Deprecation for ambiguous `+` and `-` operators.
* Deprecation for ambiguous + and - operators.
*
* This deprecation became active in Dart Sass 1.55.0.
*/
'strict-unary': Deprecation<'strict-unary'>;
/**
* Deprecation for passing invalid units to certain built-in functions.
* Deprecation for passing invalid units to built-in functions.
*
* This deprecation became active in Dart Sass 1.56.0.
*/
'function-units': Deprecation<'function-units'>;
/**
* Deprecation for using `!default` or `!global` multiple times for one
* variable.
* Deprecation for using !default or !global multiple times for one variable.
*
* This deprecation became active in Dart Sass 1.62.0.
*/
@ -92,42 +93,49 @@ export interface Deprecations {
'null-alpha': Deprecation<'null-alpha'>;
/**
* Deprecation for passing percentages to the Sass `abs()` function.
* Deprecation for passing percentages to the Sass abs() function.
*
* This deprecation became active in Dart Sass 1.65.0.
*/
'abs-percent': Deprecation<'abs-percent'>;
/**
* Deprecation for using the current working directory as an implicit load
* path.
* Deprecation for using the current working directory as an implicit load path.
*
* This deprecation became active in Dart Sass 1.73.0.
*/
'fs-importer-cwd': Deprecation<'fs-importer-cwd'>;
/**
* Deprecation for certain uses of built-in `sass:color` functions.
* Deprecation for function and mixin names beginning with --.
*
* This deprecation became active in Dart Sass 1.76.0.
*/
'css-function-mixin': Deprecation<'css-function-mixin'>;
/**
* Deprecation for certain uses of built-in sass:color functions.
*
* This deprecation became active in Dart Sass 1.78.0.
*/
'color-4-api': Deprecation<'color-4-api'>;
/**
* Deprecation for using global color functions instead of loading them from
* `sass:color`.
* Deprecation for using global color functions instead of sass:color.
*
* This deprecation became active in Dart Sass 1.76.0.
* This deprecation became active in Dart Sass 1.78.0.
*/
'color-functions': Deprecation<'color-functions'>;
/**
* Deprecation for `@import` rules.
* Deprecation for @import rules.
*
* This deprecation is not yet active, but will be soon.
*/
import: Deprecation<'import'>;
// END AUTOGENERATED LIST
/**
* Used for any user-emitted deprecation warnings.
*/

107
package-lock.json generated
View File

@ -8,6 +8,7 @@
"dependencies": {
"@types/diff": "^5.0.1",
"@types/glob": "^7.1.4",
"@types/js-yaml": "^4.0.3",
"@types/marked": "^4.0.8",
"@types/node": "^14.11.2",
"@types/prettier": "^2.4.1",
@ -17,6 +18,7 @@
"gts": "^3.1.0",
"immutable": "^4.0.0",
"indent-string": "^4.0.0",
"js-yaml": "^4.1.0",
"markdown-link-check": "3.11.1",
"markdown-toc": "^1.2.0",
"markdownlint-cli2": "^0.8.1",
@ -171,6 +173,18 @@
"node": ">= 4"
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@ -326,6 +340,11 @@
"@types/node": "*"
}
},
"node_modules/@types/js-yaml": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="
},
"node_modules/@types/json-schema": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
@ -1388,6 +1407,18 @@
"node": ">= 4"
}
},
"node_modules/eslint/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/espree": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
@ -1814,6 +1845,18 @@
"node": ">=0.10.0"
}
},
"node_modules/gray-matter/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/gts": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/gts/-/gts-3.1.1.tgz",
@ -2210,17 +2253,21 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/js-yaml/node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@ -4311,6 +4358,15 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
}
}
},
@ -4435,6 +4491,11 @@
"@types/node": "*"
}
},
"@types/js-yaml": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
"integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="
},
"@types/json-schema": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
@ -5065,6 +5126,15 @@
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
}
}
},
@ -5473,6 +5543,17 @@
"extend-shallow": "^2.0.1",
"js-yaml": "^3.8.1",
"toml": "^2.3.2"
},
"dependencies": {
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
}
}
},
"gts": {
@ -5761,12 +5842,18 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
"argparse": "^2.0.1"
},
"dependencies": {
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
}
}
},
"json-parse-even-better-errors": {

View File

@ -13,12 +13,14 @@
"toc-check": "npx ts-node test/toc-check.ts",
"update-toc": "npx ts-node tool/update-toc.ts",
"js-api-doc-check": "npm run tangle && npx ts-node test/js-api-doc-check.ts",
"deprecations-check": "npx ts-node test/deprecations-check.ts",
"typedoc": "npm run tangle && npx typedoc --treatWarningsAsErrors js-api-doc/index.d.ts",
"tangle": "npx ts-node tool/tangle.ts",
"untangle": "npx ts-node tool/untangle.ts",
"sync-deprecations": "npx ts-node tool/sync-deprecations.ts",
"markdownlint": "npx markdownlint-cli2 '**/*.md' '*.md' --ignore 'node_modules/**'",
"fix": "npm run update-toc && npm run markdownlint -- --fix && npm run tangle && gts fix && npm run untangle",
"test": "npm run tangle && gts lint && tsc --noEmit && npm run toc-check && npm run link-check && npm run js-api-doc-check && npm run typedoc"
"fix": "npm run sync-deprecations && npm run update-toc && npm run markdownlint -- --fix && npm run tangle && gts fix && npm run untangle",
"test": "npm run deprecations-check && npm run tangle && gts lint && tsc --noEmit && npm run toc-check && npm run link-check && npm run js-api-doc-check && npm run typedoc"
},
"dependencies": {
"@types/diff": "^5.0.1",
@ -41,6 +43,7 @@
"ts-dedent": "^2.2.0",
"ts-node": "^10.2.1",
"typedoc": "^0.24.7",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"yaml": "^2.2.1"
}
}

View File

@ -0,0 +1,146 @@
# Interleaved Declarations: Draft 1
*([Issue](https://github.com/sass/sass/issues/3846))*
## Table of Contents
* [Background](#background)
* [Summary](#summary)
* [Definitions](#definitions)
* [Current Style Rule](#current-style-rule)
* [Current Keyframe Block](#current-keyframe-block)
* [Declarations](#declarations)
* [Semantics](#semantics)
* [Unknown At-Rules](#unknown-at-rules)
* [Semantics](#semantics-1)
* [Deprecation Process](#deprecation-process)
## Background
> This section is non-normative.
Historically, Sass has hoisted declarations that appear after nested rules into
the original style rule. Although this doesn't match the logical ordering of the
source document, it *does* avoid duplicating the style rule (and any other
nested rules) for each set of interleaved properties.
Many years after this decision was made, the [CSS Nesting Module] was published
by the W3C, adding native support for nesting rules within style rules using
essentially the same syntax as Sass. Although initially its semantics for
interleaved declarations were the same as Sass's, [a later update] (four days
ago at time of writing) changed it to preserve the logical ordering of
interleaved declarations.
[CSS Nesting Module]: https://drafts.csswg.org/css-nesting/
[a later update]: https://github.com/w3c/csswg-drafts/commit/e5547b96f5de6fb5a68d050f42d562c448b99d0b
## Summary
> This section is non-normative.
This proposal changes the behavior of declarations that appear after nested
rules. Instead of being hoisted to the beginning of the parent rule, they will
now be placed in their original logical order, duplicating the parent selector.
That is, the following Sass:
```scss
.a {
color: red;
@media screen {color: blue}
color: green;
}
```
will now compile to this CSS:
```scss
.a {
color: red;
}
@media screen {
.a {
color: blue;
}
}
.a {
color: green;
}
```
## Definitions
### Current Style Rule
Change the definition of the [current style rule][old] to:
[old]: ../spec/style-rules.md#current-style-rule
> Differences are highlighted in bold.
The *current style rule* is the CSS style rule that was created by the innermost
[execution of a style rule], `@media` rule, `@supports` rule, unknown at-rule.
**This may be overridden by the [execution of a declaration].**
[execution of a style rule]: ../spec/style-rules.md#semantics
[execution of a declaration]: #semantics
### Current Keyframe Block
Change the definition of the [current keyframe block][old keyframe] to:
[old keyframe]: ../spec/style-rules.md#current-keyframe-block
> Differences are highlighted in bold.
The *current keyframe block* is the CSS keyframe block that was created by the
innermost [execution of a style rule]. **This may be overridden by the
[execution of a declaration].**
## Declarations
### Semantics
Add the following to the semantics for [executing a declaration] `declaration`
before "Append `css` to `parent`":
[executing a declaration]: ../spec/declarations.md#semantics
* If `parent` isn't the last statement in its parent:
* Let `copy` by a copy of `parent` without any children.
* Append `copy` to `parent`'s parent.
* Set the [current style rule], [keyframe block], or at-rule (according to
`copy`'s type) to `copy`, for the remaining duration of its previous value.
* Set `parent` to `copy`.
[current style rule]: #current-style-rule
[keyframe block]: #current-keyframe-block
## Unknown At-Rules
### Semantics
Add the following to the semantics for executing an unknown at-rule `rule` after
"If `rule`'s name is 'font-face', or if its unprefixed name is 'keyframes',
append `css` to the current module's CSS":
* If `rule`'s name is case-insensitively equal to "nest", append `css` to
`parent`.
## Deprecation Process
This proposal's change to the semantics of interleaved declarations is
backwards-incompatible. Before changing to the new semantics, an implementation
should have a period of deprecation in which it emits a deprecation warning for
a declaration whose `parent` is not the last statement in [the current module]'s
CSS without changing the existing behavior.
> Authors can move interleaved declarations before any nested rules to preserve
> existing behavior, or nest them in `& { ... }` style rules to anticipate the
> new behavior.

View File

@ -160,7 +160,13 @@ that includes CSS for *all* modules transitively used or forwarded by
* Let `selector` be `extend(rule's selector, domestic's extensions)`.
* Let `selector-lists` be an empty set of selector lists.
* Remove from `selector` any [complex selectors] containing a placeholder
selector that begins with `-` or `_` from `css`'s selector.
* If `selector` is empty, move on to the next `rule`.
* Let `selector-lists` be a set of selector lists containing only
`selector`.
* For each module `foreign` in `downstream`:
@ -171,7 +177,7 @@ that includes CSS for *all* modules transitively used or forwarded by
> means that `foreign`'s own extensions will already have been resolved
> by the time we start working on modules upstream of it.
* Add `selector` to `selector-lists`.
* Add `extended-selector` to `selector-lists`.
* Set `new-selectors[rule]` to a selector that matches the union of all
elements matched by selectors in `selector-lists`. This selector must obey
@ -188,6 +194,12 @@ that includes CSS for *all* modules transitively used or forwarded by
> `domestic`'s extensions don't count as "original", and may be optimized
> away.
If none of the selectors in `selector-lists` match any elements, add
nothing to `new-selectors`.
> This may occur if `selector-lists` contains placeholder selectors that
> haven't been extended.
* For every extension `extension` whose extender appears in `rule`'s
selector:
@ -203,7 +215,13 @@ that includes CSS for *all* modules transitively used or forwarded by
* If `domestic` has already been traversed, do nothing.
* Otherwise, traverse every module in `domestic`'s dependencies.
* For each module `upstream` in `domestic`'s dependencies:
* For each unmarked comment in `domestic`'s CSS, if that comment originally
appeared before the `@use` or `@forward` rule that loaded `upstream`, add
a copy of that comment to `css` and then mark it.
* Traverse `upstream`.
> Because this traverses modules depth-first, it emits CSS in reverse
> topological order.
@ -212,22 +230,30 @@ that includes CSS for *all* modules transitively used or forwarded by
statements in `domestic`'s CSS tree that contains only comments and
`@import` rules *and* that ends with an `@import` rule.
* Insert a copy of `initial-imports` in `css` after the last `@import` rule, or
at the beginning of `css` if it doesn't contain any `@import` rules.
* Insert a copy of `initial-imports` in `css` after the longest initial
subsequence of comments and `@import` rules in `css`.
> If there are no comments or `@import` rules in `css`, this initial
> subsequence is empty and `initial-imports` is inserted at the beginning of
> `css`.
* For each top-level statement `statement` in `domestic`'s CSS tree after
`initial-imports`:
* If `statement` is an `@import` rule, insert a copy of `statement` in `css`
after the last `@import` rule, or at the beginning of `css` if it doesn't
contain any `@import` rules.
* If `statement` is a marked comment, ignore it.
* Otherwise, add a copy of `statement` to the end of `css`, with any style
rules' selectors replaced with the corresponding selectors in
`new-selectors`.
`new-selectors`. Omit any style rules that don't have corresponding
selectors in `new-selectors`.
> This will omit style rules that contain un-extended placeholder
> selectors.
* Return `css`.
[complex selectors]: https://drafts.csswg.org/selectors-4/#complex
### Extending a Selector
This algorithm takes a selector list `extendee`, a simple selector `target`, and

View File

@ -22,6 +22,8 @@ To execute a `@function` rule `rule`:
* Let `name` be the value of `rule`'s `Identifier`.
* If `name` begins with `--`, throw an error.
* If `name` is `calc`, `element`, `expression`, `url`, `and`, `or`, or `not`, or
if `name` has a [vendor prefix] and the unprefixed identifier is one of those
strings, throw an error.

View File

@ -109,8 +109,8 @@ To execute an `@import` rule `rule`:
> `ImportMedia` is a subset of the valid syntax of `MediaQueryList`, so
> this will always work.
* Add an `@import` with the evaluated modifiers to [the current module]'s
CSS AST.
* Add an `@import` with the evaluated modifiers to [the current module]'s CSS
AST after the longest initial subsequence of comments and `@import` rules.
* Otherwise, let `file` be the result of [loading the file][] with
`argument`'s URL string. If this returns null, throw an error.

View File

@ -31,6 +31,8 @@ To execute a `@mixin` rule `rule`:
* Let `name` be the value of `rule`'s `Identifier`.
* If `name` begins with `--`, throw an error.
* Let `parent` be the [current scope].
[current scope]: ../spec.md#scope

View File

@ -1,11 +1,13 @@
# Unknown At-Rules
In order to be flexible in its compatibility with future additions to CSS, Sass
supports *all* at-rule names with a default syntax that's highly liberal in the
structures it allows. It uses the following grammar:
## Syntax
> In order to be flexible in its compatibility with future additions to CSS, Sass
> supports *all* at-rule names with a default syntax that's highly liberal in the
> structures it allows.
<x><pre>
**UnknownAtRule** ::= '@' [InterpolatedIdentifier][] InterpolatedValue?
**UnknownAtRule** ::= '@' [InterpolatedIdentifier] InterpolatedValue?
&#32; ('{' Statements '}')?
</pre></x>
@ -14,11 +16,50 @@ structures it allows. It uses the following grammar:
No whitespace is allowed after `@`. As with all statements, an `UnknownAtRule`
without a block must be separated from other statements with a semicolon.
When an at-rule is executed, its name is evaluated to produce an unquoted string
which is used as the name of the generated at-rule. Then that generated name is
checked to see if it's an at-rule that has special runtime handling.
## Semantics
> Note that only `@keyframes` has special runtime handling that's triggered
> here. Other CSS at-rules that Sass handles specially, like `@media` or
> `@supports`, are detected at parse-time. This means that `@m#{ed}ia` will be
> treated as an unknown at-rule rather than a media rule.
To execute an unknown at-rule `rule`:
* Let `name` be the result of evaluating `rule`'s `InterpolatedIdentifier`.
* Let `value` be the result of evaluating `rule`'s `InterpolatedValue`, if it
exists.
* Let `css` be a CSS unknown at-rule with name `name`, value `value`, and with
an empty list of children if `rule` has `Statements`.
* Let `parent` be the [current style rule], [keyframe block], or at-rule if one
exists; or the innermost if multiple exist.
[current style rule]: ../style-rules.md#current-style-rule
[keyframe block]: ../style-rules.md#current-style-rule
* If `rule` has `Statements`:
* If `parent` isn't set, append `css` to [the current module]'s CSS.
* Otherwise, if `parent` is a style rule:
* If `rule`'s name is "font-face", or if its [unprefixed] name is
"keyframes", append `css` to [the current module]'s CSS.
* Otherwise:
* Append `css` to `parent`'s parent.
* Append a copy of `parent` without any children to `css`.
> This copy is now the [current style rule] until `rule` is done being
> executed.
* Otherwise, append `css` to `parent`.
* Evaluate each child in `rule`'s `Statement`s.
* Otherwise:
* Append `css` to `parent` if it's set, or to [the current module]'s CSS
otherwise.
[the current module]: ../spec.md#current-module
[unprefixed]: ../syntax.md#vendor-prefix

79
spec/declarations.md Normal file
View File

@ -0,0 +1,79 @@
## Declarations
## Table of Contents
* [Syntax](#syntax)
* [Definitions](#definitions)
* [Current Declaration Name](#current-declaration-name)
* [Semantics](#semantics)
## Syntax
<x><pre>
**Declaration** ::= StandardDeclaration | CustomDeclaration
**StandardDeclaration** ::= [InterpolatedIdentifier]¹ ':' (Value | Value? '{' Statements '}')
**CustomDeclaration** ::= [InterpolatedIdentifier]² ':' InterpolatedDeclarationValue
</pre></x>
1. This may not begin with "--".
2. This *must* begin with "--".
[InterpolatedIdentifier]: syntax.md#interpolatedidentifier
## Definitions
### Current Declaration Name
The *current declaration name* is the innermost value declared as such when
[executing a declaration].
[executing a declaration]: #semantics
### Semantics
To execute a declaration `declaration`:
* Let `parent-name` be the [current declaration name], if one exists.
[current declaration name]: #current-declaration-name
* If `name` is set and `declaration` is a [`CustomDeclaration`], throw an error.
[`CustomDeclaration`]: #syntax
* Let `name` be the result of evaluating the all interpolation in
`declaration`'s name.
* If `parent-name` exists, set `name` to `parent-name + "-" + name`.
* Declare `name` as the [current declaration name] for the duration of executing
`declaration`.
* If `declaration` has a `Value`:
* Let `value` be the result of evaluating that `Value`.
* Otherwise, if `declaration` is a `CustomDeclaration`:
* Let `value` be an unquoted string whose value is the result of evaluating
`declaration`'s `InterpolatedDeclarationValue`.
* If `value` is empty, throw an error.
> Note that `value` being only whitespace is allowed, including `--foo: ;`.
* Let `parent` be the [current style rule], [keyframe block], or at-rule; or
the innermost if multiple exist.
[current style rule]: style-rules.md#current-style-rule
[keyframe block]: style-rules.md#current-style-rule
> Parsing guarantees that a declaration will have at least one parent.
* If `value` is set, and it's neither null nor an empty unquoted string:
* Let `css` be a CSS declaration with name `name` and value `value`.
* Append `css` to `parent`.
* Evaluate each child in `declaration`'s `Statements` if it exists.

124
spec/deprecations.yaml Normal file
View File

@ -0,0 +1,124 @@
# This file specifies the status of all of Sass's deprecations in a
# machine-readable way. After updating this file, run
# `npm run sync-deprecations` to update the JS API spec and docs in this repo.
#
# The Dart Sass compiler and the Node embedded host use this file to generate
# their lists of deprecations. Other embedded hosts should do the same if they
# expose the list of deprecations in their APIs.
#
# example-id:
# description: One-line description of this deprecation.
# dart-sass:
# # Status of this deprecation in Dart Sass
# status: future | active | obsolete
# # Dart Sass version that deprecated this feature.
# deprecated: 1.0.0
# # Dart Sass version that obsoleted this deprecation.
# obsolete: 2.0.0
call-string:
description: Passing a string directly to meta.call().
dart-sass:
status: active
deprecated: 0.0.0
elseif:
description: "@elseif."
dart-sass:
status: active
deprecated: 1.3.2
moz-document:
description: "@-moz-document."
dart-sass:
status: active
deprecated: 1.7.2
relative-canonical:
description: Imports using relative canonical URLs.
dart-sass:
status: active
deprecated: 1.14.2
new-global:
description: Declaring new variables with !global.
dart-sass:
status: active
deprecated: 1.17.2
color-module-compat:
description: Using color module functions in place of plain CSS functions.
dart-sass:
status: active
deprecated: 1.23.0
slash-div:
description: / operator for division.
dart-sass:
status: active
deprecated: 1.33.0
bogus-combinators:
description: Leading, trailing, and repeated combinators.
dart-sass:
status: active
deprecated: 1.54.0
strict-unary:
description: Ambiguous + and - operators.
dart-sass:
status: active
deprecated: 1.55.0
function-units:
description: Passing invalid units to built-in functions.
dart-sass:
status: active
deprecated: 1.56.0
duplicate-var-flags:
description: Using !default or !global multiple times for one variable.
dart-sass:
status: active
deprecated: 1.62.0
null-alpha:
description: Passing null as alpha in the $PLATFORM API.
dart-sass:
status: active
deprecated: 1.62.3
abs-percent:
description: Passing percentages to the Sass abs() function.
dart-sass:
status: active
deprecated: 1.65.0
fs-importer-cwd:
description: Using the current working directory as an implicit load path.
dart-sass:
status: active
deprecated: 1.73.0
css-function-mixin:
description: Function and mixin names beginning with --.
dart-sass:
status: active
deprecated: 1.76.0
color-4-api:
description: Certain uses of built-in sass:color functions.
dart-sass:
status: active
deprecated: 1.78.0
color-functions:
description: Using global color functions instead of sass:color.
dart-sass:
status: active
deprecated: 1.78.0
import:
description: "@import rules."
dart-sass:
status: future

View File

@ -192,6 +192,15 @@ message InboundMessage {
// `null` instead.
string error = 3;
}
// Whether `containing_url` in `CanonicalizeRequest` is unused.
//
// The compiler can cache the `CanonicalizeResponse` if the `containing_url`
// is unused.
//
// The default value is `false`, thus when the value is not set by the host,
// the `CanonicalizeResponse` will not be cached by the compiler.
bool containing_url_unused = 4;
}
// A response indicating the result of importing a canonical URL.
@ -257,6 +266,15 @@ message InboundMessage {
// An error message explaining why the URL could not be loaded.
string error = 3;
}
// Whether `containing_url` in `FileImportRequest` is unused.
//
// The compiler can cache the `FileImportResponse` if the `containing_url`
// is unused.
//
// The default value is `false`, thus when the value is not set by the host,
// the `FileImportResponse` will not be cached by the compiler.
bool containing_url_unused = 4;
}
// A response indicating the result of calling a custom Sass function defined

View File

@ -28,6 +28,8 @@
### `Deprecations`
<!-- START AUTOGENERATED LIST -->
<!-- Checksum: eb5e3889156f5ed12e119f11786f06f91a5f05c2 -->
```ts
export interface Deprecations {
'call-string': Deprecation<'call-string'>;
@ -44,12 +46,14 @@ export interface Deprecations {
'null-alpha': Deprecation<'null-alpha'>;
'abs-percent': Deprecation<'abs-percent'>;
'fs-importer-cwd': Deprecation<'fs-importer-cwd'>;
'css-function-mixin': Deprecation<'css-function-mixin'>;
'color-4-api': Deprecation<'color-4-api'>;
'color-functions': Deprecation<'color-functions'>;
import: Deprecation<'import'>;
'user-authored': Deprecation<'user-authored', 'user'>;
}
```
<!-- END AUTOGENERATED LIST -->
### `DeprecationOrId`

View File

@ -156,8 +156,9 @@ Before beginning compilation:
* Let `name` be `signature`'s <ident-token>.
* If there's already a global function whose name is underscore-insensitively
equal to `name`, continue to the next key/value pair.
* If there's already a global function whose name is
[underscore-insensitively] equal to `name`, continue to the next key/value
pair.
* Otherwise, add a global function whose signature is `signature`. When this
function is called:
@ -182,6 +183,7 @@ Before beginning compilation:
replaced with the result of [simplifying] those calculations.
[<ident-token>]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
[underscore-insensitively]: ../modules.md#underscore-insensitive
[`SassFunction`]: value/function.d.ts.md
[simplifying]: https://github.com/sass/sass/tree/main/spec/types/calculation.md#simplifying-a-calculation

View File

@ -15,6 +15,7 @@
* [Package Importers](#package-importers)
* [Node Package Importer](#node-package-importer)
* [Global Importer List](#global-importer-list)
* [Underscore-Insensitive](#underscore-insensitive)
* [Basename](#basename)
* [Dirname](#dirname)
* [Syntax](#syntax)
@ -70,7 +71,10 @@ A new *configuration* ID is unique unless otherwise specified.
A *module* is a collection of various properties:
* A set of [members](#member) that contains at most one member of any given type
and name.
and [underscore-insensitive] name. All lookups in this set are
underscore-insensitive unless explicitly specified otherwise.
[underscore-insensitive]: #underscore-insensitive
> For example, a module may not have two variables named `$name`, although it
> may contain a function and a mixin with the same name or two functions with
@ -300,6 +304,16 @@ When the Node Package Importer is invoked with a string named `string`:
The *global importer list* is a list of importers that's set for the entire
duration of a Sass compilation.
### Underscore-Insensitive
If a name comparison is *underscore-insensitive*, it considers the characters
U+002D HYPHEN-MINUS and U+005F LOW LINE to be equal to one another.
> Long ago, Sass only supported underscores as separators for Sass identifiers.
> When support for hyphens was added to match CSS, they were made equivalent to
> underscores for backwards-compatibility. This insensitivity is generally only
> used for Sass member names, not any other identifiers.
### Basename
The *basename* of a URL is the final component of that URL's path.

View File

@ -10,6 +10,8 @@
* [Bogus Selector](#bogus-selector)
* [Syntax](#syntax)
* [`ComplexSelector`](#complexselector)
* [Serialization](#serialization)
* [Parent Selector](#parent-selector)
## Definitions
@ -67,3 +69,12 @@ A selector list is *bogus* if any of its complex selectors are bogus.
</pre></x>
[\<combinator>]: https://drafts.csswg.org/selectors-4/#typedef-combinator
## Serialization
### Parent Selector
To serialize a parent selector, emit the character `&`.
> A parent selector can only appear in a serialized selector if it was parsed
> from plain CSS, which doesn't allow it to have a suffix.

View File

@ -4,6 +4,7 @@
* [Definitions](#definitions)
* [Current Style Rule](#current-style-rule)
* [Current Keyframe Block](#current-keyframe-block)
* [Semantics](#semantics)
## Definitions
@ -11,42 +12,77 @@
### Current Style Rule
The *current style rule* is the CSS style rule that was created by the innermost
[execution of a style rule](#semantics).
[execution of a style rule](#semantics), `@media` rule, `@supports` rule, or
unknown at-rule.
### Current Keyframe Block
The *current keyframe block* is the CSS keyframe block that was created by the
innermost [execution of a style rule](#semantics).
## Semantics
To execute a style rule `rule`:
* Let `selector` be the result of evaluating all interpolation in `rule`'s
selector and parsing the result as a selector list.
* Let `selector-text` be the result of evaluating all interpolation in `rule`'s
selector.
* If there is a [current style rule](#current-style-rule):
* Let `parent` be the [current style rule], at-rule, or keyframe block if one
exists, or the innermost if multiple exist.
* If `selector` contains one or more parent selectors, replace them with the
current style rule's selector and set `selector` to the result.
[current style rule]: #current-style-rule
* If `parent` is a keyframe block, throw an error.
* Otherwise, if `parent` is an unknown at-rule whose name without vendor
prefixes is "keyframes":
* Let `selector` be the result of parsing `selector-text` as a keyframe
selector.
* Append a keyframe block with selector `selector` to `parent`.
* Evaluate each child of `rule`.
* Cease evaluating `rule`.
* Otherwise, if `parent` is a style rule whose stylesheet wasn't [parsed as
CSS]:
[parsed as CSS]: syntax.md#parsing-text-as-css
> Checking whether `rule`'s stylesheet is CSS ensures that the plain CSS
> behavior occurs even when plain CSS is evaluated in a Sass context, such as
> through a nested `@import` or a `meta.load-css()` call.
* If `selector` contains one or more parent selectors and `rule`'s stylesheet
wasn't [parsed as CSS], replace those parent selectors with the current
style rule's selector and set `selector` to the result.
* Otherwise, nest `selector` within the current style rule's selector using
the [descendant combinator][] and set `selector` to the result.
the [descendant combinator] and set `selector` to the result.
[descendant combinator]: https://www.w3.org/TR/selectors-3/#descendant-combinators
* Otherwise, if `selector` contains one or more parent selectors, throw an
error.
* Otherwise, if `selector` contains one or more parent selectors and `rule`'s
stylesheet wasn't [parsed as CSS], throw an error.
* Let `css` be a CSS style rule with selector `selector`.
* Execute each child `child` of `rule`.
* If `parent` is set and its stylesheet was [parsed as CSS], append `css` to
`parent`.
* Otherwise, if there is a current at-rule, append `css` to its children.
* Otherwise, append `css` to [the current module]'s CSS.
[the current module]: spec.md#current-module
* Execute each child of `rule`.
* If `css` contains any children and `selector` is [bogus], throw an error.
[bogus]: selectors.md#bogus-selector
* Remove any [complex selectors][] containing a placeholder selector that
begins with `-` or `_` from `css`'s selector.
[complex selectors]: https://drafts.csswg.org/selectors-4/#complex
* Unless `css`'s selector is now empty, append `css` to [the current module][]'s
* Otherwise, if `css` contains no children, remove it from the current module's
CSS.
[the current module]: spec.md#current-module

View File

@ -210,9 +210,9 @@ modifications. The following productions should produce errors:
* A declaration followed by an open curly brace (that is, a nested declaration).
* A style rule appearing within another style rule.
* The parent selector `&` in a declaration value.
* The parent selector `&`, either in a selector or a declaration value.
* A style rule whose selector contains a trailing combinator.
* Placeholder selectors.
@ -275,6 +275,11 @@ SCSS:
adjacent `/`s in a [`SlashListExpression`] may have no whitespace between
them, so `//` is parsed as two slash separators in a slash-separated list.
* A `ParentSelector` may appear anywhere in a `CompoundSelector`, rather than
just as the first `SimpleSelector`.
* A `ParentSelector` may not have a `suffix`.
### Consuming an Identifier
This algorithm consumes input from a stream of [code points][] and returns a

View File

@ -0,0 +1,22 @@
import * as crypto from 'crypto';
import * as fs from 'fs';
const yamlFile = 'spec/deprecations.yaml';
const specFile = 'spec/js-api/deprecations.d.ts.md';
const docFile = 'js-api-doc/deprecations.d.ts';
(async () => {
const yamlText = fs.readFileSync(yamlFile, 'utf8');
const specText = fs.readFileSync(specFile, 'utf8');
const docText = fs.readFileSync(docFile, 'utf8');
const checksum = crypto.createHash('sha1').update(yamlText).digest('hex');
if (
!specText.includes(`<!-- Checksum: ${checksum} -->`) ||
!docText.includes(`// Checksum: ${checksum}`)
) {
console.error('Deprecations out-of-sync. Run `npm run sync-deprecations`.');
process.exitCode = 1;
}
})();

100
tool/sync-deprecations.ts Normal file
View File

@ -0,0 +1,100 @@
// Updates the JS API spec and docs with the list of deprecations specified
// in spec/deprecations.yaml.
import * as colors from 'colors/safe';
import * as crypto from 'crypto';
import * as fs from 'fs';
import {parse} from 'yaml';
interface YamlData {
[key: string]: {
description: string;
'dart-sass': {
status: 'active' | 'future' | 'obsolete';
deprecated?: string;
obsolete?: string;
};
};
}
const yamlFile = 'spec/deprecations.yaml';
const specFile = 'spec/js-api/deprecations.d.ts.md';
const docFile = 'js-api-doc/deprecations.d.ts';
const specRegex =
/<!-- START AUTOGENERATED LIST -->[\s\S]*?<!-- END AUTOGENERATED LIST -->/m;
const docRegex =
/\/\/ START AUTOGENERATED LIST[\s\S]*?\/\/ END AUTOGENERATED LIST/m;
(async () => {
const yamlText = fs.readFileSync(yamlFile, 'utf8');
const oldSpecText = fs.readFileSync(specFile, 'utf8');
const oldDocText = fs.readFileSync(docFile, 'utf8');
if (!specRegex.test(oldSpecText)) {
console.error(
`${colors.red(colors.bold('Error:'))} ` +
`Cannot find AUTOGENERATED LIST block in ${specFile}`
);
process.exitCode = 1;
return;
}
if (!docRegex.test(oldDocText)) {
console.error(
`${colors.red(colors.bold('Error:'))} ` +
`Cannot find AUTOGENERATED LIST block in ${docFile}`
);
process.exitCode = 1;
return;
}
const deprecations = parse(yamlText) as YamlData;
let specList = '';
let docList = '';
for (const [id, deprecation] of Object.entries(deprecations)) {
const key = id.includes('-') ? `'${id}'` : id;
specList += ` ${key}: Deprecation<'${id}'>;\n`;
const lowercase =
deprecation.description.substring(0, 1).toLowerCase() +
deprecation.description.substring(1);
const dartSass = deprecation['dart-sass'];
const activeText = !dartSass.deprecated
? 'This deprecation is not yet active, but will be soon.'
: dartSass.deprecated === '0.0.0'
? 'This deprecation was active in the first version of Dart Sass.'
: `This deprecation became active in Dart Sass ${dartSass.deprecated}.`;
const obsoleteText = dartSass.obsolete
? `\nIt became obsolete in Dart Sass ${dartSass.obsolete}.`
: '';
docList += ` /**
* Deprecation for ${lowercase.replace(/\$PLATFORM/g, 'JS')}
*
* ${activeText}${obsoleteText}
*/
${key}: Deprecation<'${id}'>;\n\n`;
}
const checksum = crypto.createHash('sha1').update(yamlText).digest('hex');
const newSpecText = oldSpecText.replace(
/<!-- START AUTOGENERATED LIST -->[\s\S]*?<!-- END AUTOGENERATED LIST -->/m,
`<!-- START AUTOGENERATED LIST -->
<!-- Checksum: ${checksum} -->
\`\`\`ts
export interface Deprecations {
${specList} 'user-authored': Deprecation<'user-authored', 'user'>;
}
\`\`\`
<!-- END AUTOGENERATED LIST -->`
);
const newDocText = oldDocText.replace(
/\/\/ START AUTOGENERATED LIST[\s\S]*?\/\/ END AUTOGENERATED LIST/m,
`// START AUTOGENERATED LIST
// Checksum: ${checksum}
${docList} // END AUTOGENERATED LIST`
);
fs.writeFileSync(specFile, newSpecText);
fs.writeFileSync(docFile, newDocText);
})();