2023-04-07 22:21:48 +00:00
|
|
|
# Sass Specification
|
2019-11-27 01:34:49 +00:00
|
|
|
|
|
|
|
This directory contains the formal specification for the Sass language.
|
|
|
|
|
|
|
|
Sass is a *living specification*, which means that it's actively updated over
|
|
|
|
time without having distinctions between numbered versions. Different
|
|
|
|
implementations may support different subsets of the specification, although all
|
|
|
|
implementations are expected to work towards full support. The *reference
|
|
|
|
implementation* (currently [Dart Sass][]) will generally support as close to the
|
|
|
|
full spec as possible.
|
|
|
|
|
|
|
|
[Dart Sass]: https://sass-lang.com/dart-sass
|
|
|
|
|
|
|
|
This specification is incomplete, and is added to *lazily*. This means that
|
|
|
|
portions of the spec are only written when they're necessary as background for
|
|
|
|
new language proposals. The Sass team eventually hopes to specify every part of
|
|
|
|
the language this way.
|
|
|
|
|
|
|
|
## Table of Contents
|
|
|
|
|
|
|
|
* [Definitions](#definitions)
|
2023-10-06 23:46:51 +00:00
|
|
|
* [Scope](#scope)
|
|
|
|
* [Global Scope](#global-scope)
|
2019-11-27 01:34:49 +00:00
|
|
|
* [Current Source File](#current-source-file)
|
|
|
|
* [Current Configuration](#current-configuration)
|
|
|
|
* [Current Import Context](#current-import-context)
|
|
|
|
* [Current Module](#current-module)
|
2023-10-06 23:46:51 +00:00
|
|
|
* [Procedures](#procedures)
|
|
|
|
* [Running in a New Scope](#running-in-a-new-scope)
|
2019-11-27 01:34:49 +00:00
|
|
|
* [Semantics](#semantics)
|
|
|
|
* [Compiling a Path](#compiling-a-path)
|
|
|
|
* [Compiling a String](#compiling-a-string)
|
|
|
|
* [Executing a File](#executing-a-file)
|
|
|
|
|
|
|
|
## Definitions
|
|
|
|
|
2023-10-06 23:46:51 +00:00
|
|
|
### Scope
|
|
|
|
|
|
|
|
A *scope* is a mutable structure that contains:
|
|
|
|
|
|
|
|
* The scope's *variables*: a mapping from identifiers to SassScript values.
|
|
|
|
* The scope's *mixins*: a mapping from identifiers to mixins.
|
|
|
|
* The scope's *functions*: a mapping from identifiers to functions.
|
|
|
|
* The scope's *parent*: a reference to another scope, which may be unset.
|
|
|
|
|
|
|
|
One scope at a time is designated the *current scope*. By default, this is the
|
|
|
|
[global scope](#global-scope).
|
|
|
|
|
|
|
|
### Global Scope
|
|
|
|
|
|
|
|
The *global scope* is the scope shared among the top level of all Sass files. It
|
|
|
|
has no parent.
|
|
|
|
|
2019-11-27 01:34:49 +00:00
|
|
|
### Current Source File
|
|
|
|
|
|
|
|
The *current source file* is the [source file][] that was passed to the
|
|
|
|
innermost active invocation of [Executing a File](#executing-a-file).
|
|
|
|
|
|
|
|
[source file]: syntax.md#source-file
|
|
|
|
|
|
|
|
*All current source files* refer to all the source files passed to any active
|
|
|
|
invocation of Executing a File.
|
|
|
|
|
|
|
|
### Current Configuration
|
|
|
|
|
|
|
|
The *current configuration* is the [configuration][] that was passed to the
|
|
|
|
innermost active invocation of [Executing a File](#executing-a-file).
|
|
|
|
|
|
|
|
[configuration]: modules.md#configuration
|
|
|
|
|
|
|
|
### Current Import Context
|
|
|
|
|
|
|
|
The *current import context* is the [import context][] that was passed to the
|
|
|
|
innermost active invocation of [Executing a File](#executing-a-file).
|
|
|
|
|
|
|
|
[import context]: modules.md#import-context
|
|
|
|
|
|
|
|
### Current Module
|
|
|
|
|
|
|
|
The *current module* is the [module][] that was created by the innermost active
|
|
|
|
invocation of [Executing a File](#executing-a-file).
|
|
|
|
|
|
|
|
[module]: modules.md#module
|
|
|
|
|
|
|
|
> Because a module is only made immutable (other than its variables) when
|
|
|
|
> execution has finished, the current module is always mutable.
|
|
|
|
|
2023-10-06 23:46:51 +00:00
|
|
|
## Procedures
|
|
|
|
|
|
|
|
### Running in a New Scope
|
|
|
|
|
|
|
|
To run a set of steps *in a new scope*:
|
|
|
|
|
|
|
|
* Let `parent` be the [current scope].
|
|
|
|
|
|
|
|
[current scope]: #scope
|
|
|
|
|
|
|
|
* Return the result of running the given steps with the current scope set to an
|
|
|
|
empty scope with `parent` as its parent.
|
|
|
|
|
2019-11-27 01:34:49 +00:00
|
|
|
## Semantics
|
|
|
|
|
|
|
|
### Compiling a Path
|
|
|
|
|
|
|
|
> This an entrypoint to the specification; it's up to each implementation how it
|
|
|
|
> exposes this to the user.
|
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
This algorithm takes a local filesystem path `path`, an optional list of
|
|
|
|
[importers] `importers`, and an optional list of paths `load-paths`. It returns
|
|
|
|
a string.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
|
|
|
* Let `text` be the result of decoding the binary contents of the file at
|
|
|
|
`path`.
|
|
|
|
|
|
|
|
* Let `syntax` be:
|
|
|
|
|
|
|
|
* "indented" if `path` ends in `.sass`.
|
|
|
|
* "css" if `path` ends in `.css`.
|
|
|
|
* "scss" otherwise.
|
|
|
|
|
|
|
|
* Let `url` be the absolute `file:` URL corresponding to `path`.
|
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Let `importer` be a [filesystem importer] with an arbitrary `base`.
|
|
|
|
|
|
|
|
> This importer will only ever be passed absolute URLs, so its base won't
|
|
|
|
> matter.
|
|
|
|
|
|
|
|
* Return the result of [compiling](#compiling-a-string) `text` with `syntax`,
|
|
|
|
`url`, `importer`, `importers`, and `load-paths`.
|
|
|
|
|
|
|
|
[importers]: modules.md#importer
|
2019-11-27 01:34:49 +00:00
|
|
|
|
|
|
|
### Compiling a String
|
|
|
|
|
|
|
|
> This an entrypoint to the specification; it's up to each implementation how it
|
|
|
|
> exposes this to the user.
|
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
This algorithm takes:
|
2023-08-22 21:10:27 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* a string `string`,
|
|
|
|
* a syntax `syntax` ("indented", "scss", or "css"),
|
|
|
|
* an optional URL `url`,
|
|
|
|
* an optional [importer] `importer`,
|
|
|
|
* an optional list of importers `importers`,
|
|
|
|
* and an optional list of paths `load-paths`.
|
|
|
|
|
|
|
|
[importer]: modules.md#importer
|
|
|
|
|
|
|
|
It runs as follows:
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Set the [global importer list] to `importers`.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* For each `path` in `load-paths`:
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Let `base` be the absolute `file:` URL that refers to `path`.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Add a [filesystem importer] with base `base` to the global importer list.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Let `ast` be the result of [parsing] `text` as `syntax`.
|
2019-11-27 04:32:39 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* If `url` is null:
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* If `importer` is not null, throw an error.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
* Set `url` to a unique value.
|
|
|
|
|
|
|
|
> This ensures that all source files have a valid URL. When displaying this
|
|
|
|
> value, implementations should help users understand the source of the string
|
|
|
|
> if possible.
|
|
|
|
|
2022-04-12 00:09:26 +00:00
|
|
|
* If `importer` is null:
|
|
|
|
|
|
|
|
* If `url` is a `file:` URL, set `importer` to be a [filesystem importer] with an
|
|
|
|
arbitrary `base`.
|
|
|
|
|
|
|
|
> This importer will only ever be passed absolute URLs, so its base won't
|
|
|
|
> matter.
|
|
|
|
|
|
|
|
* If `url` is not a `file:` URL, set `importer` to be a function that always
|
|
|
|
returns null.
|
2021-06-16 23:24:05 +00:00
|
|
|
|
|
|
|
* Let `file` be the [source file][] with `ast`, canonical URL `url`, and
|
|
|
|
importer `importer`.
|
2019-11-27 01:34:49 +00:00
|
|
|
|
|
|
|
* Let `module` be the result of [executing](#executing-a-file) `file`.
|
|
|
|
|
|
|
|
* Let `css` be the result of [resolving `module`'s extensions][].
|
|
|
|
|
|
|
|
[resolving `module`'s extensions]: at-rules/extend.md#resolving-a-modules-extensions
|
|
|
|
|
|
|
|
* Return the result of converting `css` to a CSS string.
|
|
|
|
|
2021-06-16 23:24:05 +00:00
|
|
|
[filesystem importer]: modules.md#filesystem-importer
|
|
|
|
[parsing]: syntax.md#parsing-text
|
|
|
|
[global importer list]: modules.md#global-importer-list
|
|
|
|
|
2019-11-27 01:34:49 +00:00
|
|
|
### Executing a File
|
|
|
|
|
|
|
|
This algorithm takes a [source file][] `file`, a [configuration][] `config`, an
|
|
|
|
[import context][] `import`, and returns a [module][].
|
|
|
|
|
|
|
|
* Let `module` be an empty module with source file `file`.
|
|
|
|
|
|
|
|
* Let `uses` be an empty map from `@use` rules to modules.
|
|
|
|
|
|
|
|
* Execute each top-level statement as described in that statement's
|
|
|
|
specification.
|
|
|
|
|
|
|
|
> The semantics for executing each statement is defined in that statement's
|
|
|
|
> individual specification.
|
|
|
|
|
|
|
|
* For each variable declaration `variable` with a `!global` flag in `file`,
|
|
|
|
whether or not it was evaluated:
|
|
|
|
|
|
|
|
* If `variable`'s name *doesn't* begin with `-` or `_` and `variable` is not
|
|
|
|
yet in `module`, set `variable` to `null` in `module`.
|
|
|
|
|
|
|
|
> This isn't necessary for implementations that follow the most recent
|
|
|
|
> [variables spec][] and don't allow `!global` assignments to variables
|
|
|
|
> that don't yet exist. However, at time of writing, all existing
|
|
|
|
> implementations are in the process of deprecating the old `!global`
|
|
|
|
> behavior, which allowed `!global` declarations to create new
|
|
|
|
> variables.
|
|
|
|
>
|
|
|
|
> Setting all `!global` variables to `null` if they weren't otherwise set
|
|
|
|
> guarantees the stability of static analysis by ensuring that the set of
|
|
|
|
> variables a module exposes doesn't depend on how it was executed.
|
2019-11-27 04:32:39 +00:00
|
|
|
|
|
|
|
[variables spec]: variables.md
|
2019-11-27 01:34:49 +00:00
|
|
|
|
|
|
|
* Return `module`. Its functions, mixins, and CSS are now immutable.
|