2016-02-12 21:03:38 +00:00
|
|
|
# The Next-Generation Sass Module System: Draft 1
|
2015-12-12 00:24:42 +00:00
|
|
|
|
|
|
|
This repository houses a proposal for the `@use` directive and associated module
|
|
|
|
system, which is intended to be the headlining feature for Sass 4. This is a
|
|
|
|
*living proposal*: it's intended to evolve over time, and is hosted on GitHub to
|
|
|
|
encourage community collaboration and contributions. Any suggestions or issues
|
|
|
|
can be brought up and discussed on [the issue tracker][issues].
|
|
|
|
|
|
|
|
[issues]: https://github.com/sass/proposal.module-system
|
|
|
|
|
2016-01-09 03:18:02 +00:00
|
|
|
Although this document describes some imperative processes when describing the
|
|
|
|
semantics of the module system, these aren't meant to prescribe a specific
|
|
|
|
implementation. Individual implementations are free to implement this feature
|
|
|
|
however they want as long as the end result is the same. However, there are
|
|
|
|
specific design decisions that were made with implementation efficiency in
|
|
|
|
mind—these will be called out explicitly in block-quoted "implementation note"s.
|
|
|
|
|
2016-01-22 21:26:29 +00:00
|
|
|
## Table of Contents
|
|
|
|
|
|
|
|
* [Background](#background)
|
|
|
|
* [Goals](#goals)
|
|
|
|
* [High-Level](#high-level)
|
|
|
|
* [Low-Level](#low-level)
|
|
|
|
* [Non-Goals](#non-goals)
|
|
|
|
* [Definitions](#definitions)
|
|
|
|
* [Member](#member)
|
2016-02-06 00:39:36 +00:00
|
|
|
* [Extension](#extension)
|
2016-01-22 21:26:29 +00:00
|
|
|
* [CSS Tree](#css-tree)
|
|
|
|
* [Configuration](#configuration)
|
|
|
|
* [Module](#module)
|
|
|
|
* [Module Graph](#module-graph)
|
|
|
|
* [Source File](#source-file)
|
|
|
|
* [Entrypoint](#entrypoint)
|
2016-02-06 00:53:54 +00:00
|
|
|
* [Import Context](#import-context)
|
2016-01-22 21:26:29 +00:00
|
|
|
* [Syntax](#syntax)
|
2016-01-22 23:34:36 +00:00
|
|
|
* [`@forward`](#forward)
|
2016-02-06 01:15:36 +00:00
|
|
|
* [Procedures](#procedures)
|
2016-01-22 21:26:29 +00:00
|
|
|
* [Loading Modules](#loading-modules)
|
2016-02-06 01:15:36 +00:00
|
|
|
* [Resolving Extensions](#resolving-extensions)
|
|
|
|
* [Semantics](#semantics)
|
2016-01-22 23:53:22 +00:00
|
|
|
* [Compilation Process](#compilation-process)
|
2016-02-06 00:52:38 +00:00
|
|
|
* [Executing Files](#executing-files)
|
2016-01-22 21:26:29 +00:00
|
|
|
* [Resolving Members](#resolving-members)
|
2016-02-06 01:15:36 +00:00
|
|
|
* [Using Modules](#using-modules)
|
2016-01-23 01:37:01 +00:00
|
|
|
* [Module Mixins](#module-mixins)
|
2016-02-06 01:15:36 +00:00
|
|
|
* [Forwarding Modules](#forwarding-modules)
|
2016-02-06 00:53:54 +00:00
|
|
|
* [Importing Files](#importing-files)
|
2016-02-12 20:57:25 +00:00
|
|
|
* [Built-In Modules](#built-in-modules)
|
2016-01-22 21:26:29 +00:00
|
|
|
|
2015-12-12 00:24:42 +00:00
|
|
|
## Background
|
|
|
|
|
|
|
|
The new `@use` directive is intended to supercede Sass's `@import` directive as
|
|
|
|
the standard way of sharing styles across Sass files. `@import` is the simplest
|
|
|
|
form of re-use: it does little more than directly include the target file in the
|
|
|
|
source file. This has caused numerous problems in practice: including the same
|
|
|
|
file more than once slows down compilation and produces redundant output, users
|
|
|
|
must manually namespace everything in their libraries, and there's no
|
|
|
|
encapsulation to allow them to keep moving pieces hidden.
|
|
|
|
|
|
|
|
The new module system is intended to address these shortcomings (among others)
|
|
|
|
and bring Sass's modularity into line with the best practices as demonstrated by
|
|
|
|
other modern languages. As such, the semantics of `@use` are is heavily based on
|
|
|
|
other languages' module systems, with Python and Dart being particularly strong
|
|
|
|
influences.
|
2015-12-12 00:58:26 +00:00
|
|
|
|
2015-12-12 01:12:08 +00:00
|
|
|
## Goals
|
|
|
|
|
|
|
|
### High-Level
|
|
|
|
|
|
|
|
These are the philosophical design goals for the module system at large. While
|
|
|
|
they don't uniquely specify a system, they do represent the underlying
|
|
|
|
motivations behind many of the lower-level design decisions.
|
2015-12-12 00:58:26 +00:00
|
|
|
|
|
|
|
* **Locality**. The module system should make it possible to understand a Sass
|
|
|
|
file by looking only at that file. An important aspect of this is that names
|
|
|
|
in the file should be resolved based on the contents of the file rather than
|
|
|
|
the global state of the compilation. This also applies to authoring: an author
|
|
|
|
should be able to be confident that a name is safe to use as long as it
|
|
|
|
doesn't conflict with any name visible in the file.
|
|
|
|
|
|
|
|
* **Encapsulation**. The module system should allow authors, particularly
|
|
|
|
library authors, to choose what API they expose. They should be able to define
|
|
|
|
entities for internal use without making those entities available for external
|
2015-12-12 01:30:42 +00:00
|
|
|
users to access or modify. This also includes the ability to "forward" public
|
|
|
|
APIs from another file.
|
2015-12-12 00:58:26 +00:00
|
|
|
|
|
|
|
* **Configuration**. Sass is unusual among languages in that it encourages the
|
|
|
|
use of files whose entire purpose is to produce side effects—specifically, to
|
|
|
|
emit CSS. There's also a broader class of libraries that may not emit CSS
|
|
|
|
directly, but do define configuration variables that are used in computations,
|
|
|
|
sometimes at the top level. The module system should allow the user to
|
|
|
|
flexibly use modules with side-effects, and shouldn't force global
|
|
|
|
configuration.
|
2015-12-12 01:12:08 +00:00
|
|
|
|
|
|
|
### Low-Level
|
|
|
|
|
|
|
|
These are goals that are based less on philosophy than on practicality. For the
|
|
|
|
most part, they're derived from user feedback that we've collected about
|
|
|
|
`@import` over the years.
|
|
|
|
|
|
|
|
* **Using CSS files**. People often have CSS files that they want to bring into
|
|
|
|
their Sass compilation. Historically, `@import` has been unable to do this due
|
|
|
|
to its overlap with the plain-CSS `@import` directive and the requirement that
|
|
|
|
SCSS remain a CSS superset. With a new directive name, this becomes possible.
|
|
|
|
|
|
|
|
* **Import once**. Because `@import` is a literal textual inclusion, multiple
|
|
|
|
`@import`s of the same Sass file within the scope of a compilation will
|
|
|
|
compile and run that file multiple times. At best this hurts compilation time,
|
|
|
|
and it can also contribute to bloated CSS output when the styles themselves
|
|
|
|
are duplicated. The new module system should only compile a file once, at
|
|
|
|
least for the default configuration.
|
2015-12-12 01:29:53 +00:00
|
|
|
|
2016-02-06 00:53:54 +00:00
|
|
|
* **Backwards compatibility**. We want to make it as easy as possible for people
|
|
|
|
to migrate to the new module system, and that means making it work in
|
|
|
|
conjunction with existing stylesheets that use `@import`. Existing stylesheets
|
|
|
|
that only use `@import` should have identical importing behavior to earlier
|
|
|
|
versions of Sass, and stylesheets should be able to change parts to `@use`
|
|
|
|
without changing the whole thing at once.
|
|
|
|
|
2015-12-12 01:29:53 +00:00
|
|
|
### Non-Goals
|
|
|
|
|
|
|
|
These are potential goals that we have explicitly decided to avoid pursuing for
|
|
|
|
various reasons. Some of them may be on the table for future work, but none are
|
|
|
|
expected to land in Sass 4.
|
|
|
|
|
|
|
|
* **Dynamic imports**. Allowing the path to a module to be defined dynamically,
|
|
|
|
whether by including variables or including it in a conditional block, moves
|
|
|
|
away from being declarative. In addition to making stylesheets harder to read,
|
|
|
|
this makes any sort of static analysis more difficult—and actually impossible
|
|
|
|
in the general case. It also limits the possibility of future implementation
|
|
|
|
optimizations.
|
|
|
|
|
|
|
|
* **Importing multiple files at once**. In addition to the long-standing reason
|
|
|
|
that this hasn't been supported—that it opens authors up to sneaky and
|
|
|
|
difficult-to-debug ordering bugs—this violates the principle of locality by
|
|
|
|
obfuscating which files are imported and thus where names come from.
|
|
|
|
|
|
|
|
* **Extend-only imports**. The idea of importing a file so that the CSS it
|
|
|
|
generates isn't emitted unless it's `@extend`ed is cool, but it's also a lot
|
|
|
|
of extra work. This is the most likely feature to end up in a future release,
|
|
|
|
but it's not central enough to the module system to include in Sass 4.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
|
|
|
## Definitions
|
|
|
|
|
|
|
|
### Member
|
|
|
|
|
2016-01-30 00:07:32 +00:00
|
|
|
A *member* is anything that's defined either by the user or the implementation
|
|
|
|
and is identified by a Sass identifier. This currently includes variables,
|
2016-01-09 01:02:26 +00:00
|
|
|
mixins, functions, and placeholder selectors. Each member type has its own
|
|
|
|
namespace, so for example the variable `$name` doesn't conflict with the
|
|
|
|
placeholder selector `%name`.
|
|
|
|
|
2016-01-09 03:18:02 +00:00
|
|
|
All members have definitions associated with them, whose specific structure
|
|
|
|
depends on the type of the given member. Variables, mixins, and functions have
|
|
|
|
intuitive definitions, but placeholder selectors' definitions just indicate
|
|
|
|
which [module](#module) they come from.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
2016-01-16 00:21:05 +00:00
|
|
|
There's some question of whether placeholders ought to be considered members,
|
|
|
|
and consequently [namespaced](#resolving-members) like other members. On one
|
|
|
|
hand, they're frequently used in parallel with mixins as the API exposed by a
|
|
|
|
library, which suggests that they should be namespaced like the mixins they
|
|
|
|
parallel. On the other hand, this usage is somewhat discouraged since it doesn't
|
|
|
|
treat them like selectors, and not namespacing them would potentially free up
|
|
|
|
characters like `.` or `:` to be used as namespace separators.
|
|
|
|
|
2016-02-06 00:39:36 +00:00
|
|
|
### Extension
|
|
|
|
|
|
|
|
An *extension* is an object that represents a single `@extend` rule. It contains
|
|
|
|
two selectors: the *extender* is the selector for the rule that contains the
|
|
|
|
`@extend`, and the *extendee* is the selector that comes after the `@extend`.
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```scss
|
|
|
|
.extender {
|
|
|
|
@extend .extendee;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
An extension may be applied to a selector to produce a new selector. This
|
|
|
|
process is outside the scope of this document, and remains unchanged from
|
|
|
|
previous versions of Sass.
|
|
|
|
|
2016-01-09 01:02:26 +00:00
|
|
|
### CSS Tree
|
|
|
|
|
|
|
|
A *CSS tree* is an abstract CSS syntax tree. It has multiple top-level CSS
|
|
|
|
declarations like `@`-rules or rulesets. The ordering of the roots is
|
|
|
|
significant.
|
|
|
|
|
|
|
|
A CSS tree cannot contain any Sass-specific constructs, with the notable
|
|
|
|
exception of placeholder selectors. These are allowed so that modules' CSS may
|
|
|
|
be `@extend`ed.
|
|
|
|
|
|
|
|
An empty CSS tree contains no top-level declarations.
|
|
|
|
|
|
|
|
### Configuration
|
|
|
|
|
|
|
|
A *configuration* is a set of variables with associated SassScript values. It's
|
2016-02-06 00:52:38 +00:00
|
|
|
used when [executing](#executing-files) a [source file](#source-file) to
|
|
|
|
customize its execution. It may be empty—that is, it may contain no variables.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
|
|
|
Two configurations are considered identical if they contain the same variables,
|
|
|
|
and if each pair of variables with the same name has values that are `==` to one
|
|
|
|
another.
|
|
|
|
|
|
|
|
### Module
|
|
|
|
|
2016-02-06 00:39:36 +00:00
|
|
|
A *module* is an abstract collection of [members](#members) and
|
|
|
|
[extensions](#extensions), as well as a [CSS tree](#css-tree) (although that
|
|
|
|
tree may be empty). Each module may have only one member of a given type and
|
|
|
|
name (for example, a module may not have two variables named `$name`). To
|
|
|
|
satisfy this requirement, placeholder selectors are de-duplicated.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
|
|
|
Each module is uniquely identified by the combination of a URI and a
|
2016-02-06 00:52:38 +00:00
|
|
|
[configuration](#configuration). A given module can be produced by
|
|
|
|
[executing](#executing-files) the [source file](#source-file) identified by the
|
|
|
|
module's URI with the module's configuration.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
2016-01-16 01:35:32 +00:00
|
|
|
### Module Graph
|
|
|
|
|
2016-01-22 23:34:36 +00:00
|
|
|
Modules also track their `@use` and [`@forward`](#forwarding-modules)
|
2016-01-23 01:37:01 +00:00
|
|
|
directives, which point to other modules. In this sense, modules with empty
|
|
|
|
configuration can be construed as a [directed acyclic graph][] where the
|
|
|
|
vertices are modules and the edges are `@use` directives (without `mixin`
|
|
|
|
clauses) and/or `@forward` directives. We call this the *module graph*.
|
|
|
|
|
|
|
|
[directed acyclic graph]: https://en.wikipedia.org/wiki/Directed_acyclic_graph
|
2016-01-16 01:35:32 +00:00
|
|
|
|
|
|
|
The module graph is not allowed to contain cycles because they make it
|
|
|
|
impossible to guarantee that all dependencies of a module are fully executed
|
|
|
|
before that module is loaded. Although a module's members can be determined
|
2016-02-06 00:52:38 +00:00
|
|
|
without [executing](#executing-files) it, Sass allows code to be executed while
|
|
|
|
loading a module, which means those members may be executed.
|
2016-01-16 01:35:32 +00:00
|
|
|
|
2016-01-09 01:02:26 +00:00
|
|
|
### Source File
|
|
|
|
|
2016-02-06 00:52:38 +00:00
|
|
|
A *source file* is an entity uniquely identified by a URI. It can be
|
|
|
|
[executed](#executing-files) with a [configuration](#configuration) to produce a
|
|
|
|
[module](#module). The names (and mixin and function signatures) of this
|
|
|
|
module's members are static, and can be determined without executing the file.
|
|
|
|
This means that all modules for a given source file have the same member names
|
|
|
|
regardless of the configurations used for those modules.
|
2016-01-09 01:02:26 +00:00
|
|
|
|
|
|
|
There are five types of source file:
|
|
|
|
|
|
|
|
* Sass files, SCSS files, and CSS files are identified by file paths.
|
|
|
|
|
2016-02-12 20:57:25 +00:00
|
|
|
* [Built-in modules](#built-in modules) are identified by URIs beginning with
|
|
|
|
"sass:".
|
2016-01-09 01:02:26 +00:00
|
|
|
|
|
|
|
* Implementations may define implementation-specific or pluggable means of
|
|
|
|
defining source files, which can use any URI.
|
|
|
|
|
|
|
|
Each one has different execution semantics that are beyond the scope of this
|
|
|
|
document. Note that some of these are not or may not actually be files on the
|
|
|
|
file system.
|
2016-01-09 03:18:02 +00:00
|
|
|
|
2016-01-16 03:08:27 +00:00
|
|
|
### Entrypoint
|
|
|
|
|
|
|
|
The *entrypoint* of a compilation is the [source file](#source-file) that was
|
|
|
|
initially passed to the implementation. Similarly, the *entrypoint module* is
|
|
|
|
the [module](#module) loaded from that source file with an empty configuration.
|
|
|
|
The entrypoint module is the root of the [module graph](#module-graph).
|
|
|
|
|
2016-02-06 00:53:54 +00:00
|
|
|
### Import Context
|
|
|
|
|
|
|
|
An *import context* is a collection of members, indexed by their names. It's
|
|
|
|
used to ensure that the previous global-namespace behavior is preserved when
|
|
|
|
`@import`s are used.
|
|
|
|
|
|
|
|
An import context is mutable throughout its entire lifetime, unlike a module
|
|
|
|
which doesn't change once it's been fully created. This allows it to behave as a
|
|
|
|
shared namespace for a connected group of imports.
|
|
|
|
|
2016-01-09 03:18:02 +00:00
|
|
|
## Syntax
|
|
|
|
|
|
|
|
The new directive will be called `@use`. The grammar for this directive is as
|
|
|
|
follows:
|
|
|
|
|
|
|
|
```
|
2016-01-23 01:37:01 +00:00
|
|
|
UseDirective ::= '@use' QuotedString (AsClause? MixinClause? | NoPrefix?)
|
2016-01-09 03:18:02 +00:00
|
|
|
AsClause ::= 'as' Identifier
|
|
|
|
NoPrefix ::= 'no-prefix'
|
2016-01-23 01:37:01 +00:00
|
|
|
MixinClause ::= 'mixin'
|
2016-01-09 03:18:02 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
*Note: this only encompasses the syntax whose semantics are currently described
|
|
|
|
in this document. As the document becomes more complete, the grammar will be
|
|
|
|
expanded accordingly.*
|
|
|
|
|
|
|
|
`@use` directives must be at the top level of the document, and must come before
|
|
|
|
any directives other than `@charset`. Because each `@use` directive affects the
|
|
|
|
namespace of the entire [source file](#source-file) that contains it, whereas
|
|
|
|
most other Sass constructs are purely imperative, keeping it at the top of the
|
|
|
|
file helps reduce confusion.
|
|
|
|
|
2016-01-23 01:37:01 +00:00
|
|
|
The mixin clause is not allowed for unprefixed modules because the mixin name
|
|
|
|
is derived from the module's prefix.
|
|
|
|
|
|
|
|
> **Design note:**
|
|
|
|
>
|
|
|
|
> I'm not at all sure about the mixin syntax here. `@use "foo" mixin` doesn't
|
|
|
|
> read very well, and sounds less sentence-like than I'd prefer. But I'm having
|
|
|
|
> trouble determining what else would be better, and still remain orthogonal to
|
|
|
|
> all the other modifiers that can be applied.
|
|
|
|
|
2016-01-22 23:34:36 +00:00
|
|
|
### `@forward`
|
|
|
|
|
|
|
|
This proposal introduces an additional new directive, called `@forward`. The
|
|
|
|
grammar for this directive is as follows:
|
|
|
|
|
|
|
|
```
|
|
|
|
ForwardDirective ::= '@forward' QuotedString (ShowClause | HideClause)?
|
|
|
|
ShowClause ::= 'show' Identifier (',' Identifier)*
|
|
|
|
HideClause ::= 'hide' Identifier (',' Identifier)*
|
|
|
|
```
|
|
|
|
|
|
|
|
`@forward` directives must be at the top level of the document, and must come
|
|
|
|
before any directives other than `@charset` or `@use`.
|
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
## Procedures
|
|
|
|
|
|
|
|
The following procedures are not directly tied to the semantics of any single
|
|
|
|
construct. Instead, they're used as components of multiple constructs'
|
|
|
|
semantics. They can be thought of as re-usable functions.
|
2016-01-09 03:18:02 +00:00
|
|
|
|
|
|
|
### Loading Modules
|
|
|
|
|
2016-01-22 23:53:22 +00:00
|
|
|
This describes the general process for loading a module. It's used as part of
|
2016-02-06 00:52:38 +00:00
|
|
|
various other semantics described below. To load a module with a given URI,
|
|
|
|
[configuration](#configuration):
|
2016-01-09 03:18:02 +00:00
|
|
|
|
|
|
|
* Look up the [source file](#source-file) with the given URI. The process for
|
|
|
|
doing this is out of scope of this document.
|
|
|
|
|
2016-01-22 23:53:22 +00:00
|
|
|
* If no such file can be found, loading fails.
|
2016-01-09 03:18:02 +00:00
|
|
|
|
2016-02-06 00:52:38 +00:00
|
|
|
* If the source file has already been [executed](#executing-files) with the
|
|
|
|
given configuration, use the module that execution produced. This fulfills the
|
|
|
|
"import once" low-level goal.
|
2016-01-09 03:18:02 +00:00
|
|
|
|
2016-02-06 00:52:38 +00:00
|
|
|
* If the source file is currently being executed with the given configuration,
|
|
|
|
loading fails. This disallows circular `@use`s, which ensures that modules
|
|
|
|
can't be used until they're fully initialized.
|
2016-01-22 23:53:22 +00:00
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
* Otherwise, execute that file with the given configuration, and take the
|
2016-01-09 03:18:02 +00:00
|
|
|
resulting module.
|
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
* If the source file contained a `@use` directive with a `mixin` clause and a
|
|
|
|
`@forward` directive with the same URI, and if that `@use` directive's mixin
|
|
|
|
was not included during the execution of the source file, loading fails.
|
|
|
|
|
|
|
|
* Otherwise, use the resulting module.
|
|
|
|
|
2016-01-09 03:18:02 +00:00
|
|
|
> **Implementation note:**
|
|
|
|
>
|
|
|
|
> Although this specification only requires that modules be cached and reused
|
2016-01-22 21:22:23 +00:00
|
|
|
> when compiling a single [entrypoint](#entrypoint), modules are intentionally
|
2016-01-09 03:18:02 +00:00
|
|
|
> context-independent enough to store and re-use across multiple entrypoints, as
|
|
|
|
> long as no source files change. For example, if the user requests that all
|
|
|
|
> Sass files beneath `stylesheets/sass` be compiled, modules may be shared
|
|
|
|
> between those separate compilations.
|
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
### Resolving Extensions
|
|
|
|
|
|
|
|
The module system also scopes the resolution of the `@extend` directive. This
|
|
|
|
helps satisfy locality, making selector extension more predictable than it is
|
|
|
|
using `@import`s.
|
|
|
|
|
|
|
|
Extension is scoped to CSS in [module](#module)s *transitively used* by the
|
|
|
|
module in which the `@extend` appears. This transitivity is necessary because
|
|
|
|
CSS is not considered a [member](#member) of a module, and can't be controlled
|
|
|
|
as explicitly as members can. Extending all transitively-used modules means that
|
|
|
|
the `@extend` affects exactly that CSS that is guaranteed to exist by the `@use`
|
|
|
|
directives.
|
|
|
|
|
|
|
|
We define a general process for resolving extensions for a given module (call it
|
|
|
|
the *starting module*). This process emits CSS for that module and everything it
|
|
|
|
transitively uses.
|
|
|
|
|
|
|
|
* Take the subgraph of the module graph containing modules that are transitively
|
|
|
|
reachable from the starting module. Call this the *extended graph*.
|
|
|
|
|
|
|
|
* For each module in the extended graph (call it the *domestic module*) in
|
|
|
|
reverse [topological][] order:
|
|
|
|
|
|
|
|
* Create an empty map for the domestic module (call it the module's *extended
|
|
|
|
selectors*). This map will contain selectors defined for rules in this
|
|
|
|
module and its transitively reachable modules, with extensions partially
|
|
|
|
resolved. This map is indexed by the locations of the rules for those
|
|
|
|
selectors. We say that this is the *original location* for a selector.
|
|
|
|
|
|
|
|
* For each module used or forwarded by the domestic module (call it the
|
|
|
|
*foreign module*`) in reverse [topological][] order:
|
|
|
|
|
|
|
|
* For each of the foreign module's extended selectors (call it the *foreign
|
|
|
|
selector*):
|
|
|
|
|
|
|
|
* If the domestic module has an extended selector that has the same
|
|
|
|
original location as the foreign selector, take it. Otherwise, create a
|
|
|
|
selector that matches no elements. Call this the *domestic selector*.
|
|
|
|
|
|
|
|
* Create a new selector that matches the union of all elements matched by
|
|
|
|
the foreign selector selector and the domestic selector. Call this the
|
|
|
|
*new selector*.
|
|
|
|
|
|
|
|
* If the foreign module was used by the domestic module (as opposed to
|
|
|
|
only being forwarded), apply the domestic module's extensions to the new
|
|
|
|
selector, and replace it with the result.
|
|
|
|
|
|
|
|
* Add the new selector to the domestic module's extended selectors,
|
|
|
|
indexed by the foreign selector's original location. Replace the
|
|
|
|
domestic selector if necessary.
|
|
|
|
|
|
|
|
* For each CSS rule in the domestic module:
|
|
|
|
|
|
|
|
* Apply the domestic module's extensions to the rule's selector.
|
|
|
|
|
|
|
|
* Add the resulting selector to the domestic module's extended selectors,
|
|
|
|
indexed by the rule's location.
|
|
|
|
|
|
|
|
* For each module in the extended graph (call it the *domestic module*) in
|
|
|
|
reverse [topological][] order:
|
|
|
|
|
|
|
|
* Emit each top-level CSS construct in the domestic module, with any selectors
|
|
|
|
replaced by the corresponding selector in the starting module's extended
|
|
|
|
selectors.
|
|
|
|
|
|
|
|
There is intentionally no way for a module to affect the extensions of another
|
|
|
|
module that doesn't transitively use it. This promotes locality, and matches the
|
|
|
|
behavior of mixins and functions in that monkey-patching is disallowed.
|
|
|
|
|
|
|
|
[topological]: https://en.wikipedia.org/wiki/Topological_sorting
|
|
|
|
|
|
|
|
## Semantics
|
|
|
|
|
2016-01-22 23:53:22 +00:00
|
|
|
### Compilation Process
|
|
|
|
|
|
|
|
First, let's look at the large-scale process that occurs when compiling a Sass
|
|
|
|
[entrypoint](#entrypoint) to CSS.
|
|
|
|
|
|
|
|
* [Load](#loading-modules) the [module](#module) with the entrypoint URI and the
|
|
|
|
empty configuration. Note that this transitively loads any referenced modules,
|
|
|
|
producing a [module graph](#module-graph).
|
|
|
|
|
2016-02-06 00:39:36 +00:00
|
|
|
* [Resolve extensions](#resolving-extensions) for the entrypoint's module. The
|
2016-01-29 23:29:02 +00:00
|
|
|
resulting CSS is the compilation's output.
|
2016-01-22 23:53:22 +00:00
|
|
|
|
2016-02-06 00:52:38 +00:00
|
|
|
### Executing Files
|
|
|
|
|
|
|
|
Many of the details of executing a [source file](#source-file) are out of scope
|
|
|
|
for this specification. However, certain constructs have relevant new semantics
|
|
|
|
that are covered below. This procedure should be understood as modifying and
|
|
|
|
expanding upon the existing execution process rather than being a comprehensive
|
|
|
|
replacement.
|
|
|
|
|
|
|
|
Given a [source file](#source-file), a [configuration](#configuration), and
|
|
|
|
optionally an [import context](#import-context):
|
|
|
|
|
|
|
|
* Create an empty module with the given configuration and the current file's
|
|
|
|
URI. Call this the *current module*.
|
|
|
|
|
|
|
|
* When a `@use` directive is encountered, [use the module](#using-modules) it
|
|
|
|
refers to.
|
|
|
|
|
|
|
|
* When a `@forward` directive is encountered,
|
|
|
|
[forward the module](#forwarding-modules) it refers to.
|
2016-02-06 00:53:54 +00:00
|
|
|
|
|
|
|
* When an `@import` directive is encountered,
|
|
|
|
[import the file](#importing-files) it refers to.
|
2016-02-06 00:52:38 +00:00
|
|
|
|
|
|
|
* When an `@extend` directive is encountered, add its extension to the current
|
|
|
|
module.
|
|
|
|
|
|
|
|
* When a CSS rule or a plain CSS directive is encountered, execute it as normal
|
|
|
|
and add the resulting CSS to the current module's CSS.
|
|
|
|
|
|
|
|
* When a [member](#member) definition is encountered, if its member's name
|
2016-02-06 00:53:54 +00:00
|
|
|
doesn't begin with `-` or `_`, add it to the current module. In addition, if
|
|
|
|
there's a current import context, add the member to the import context
|
|
|
|
(regardless of whether or not the member is private).
|
2016-02-06 00:52:38 +00:00
|
|
|
|
|
|
|
* When a member use is encountered, [resolve it](#resolving-members) using the
|
|
|
|
set of used modules and the current import context.
|
|
|
|
|
|
|
|
* Once all top-level statements are executed, return the current module.
|
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
Note that members that begin with `-` or `_` (which Sass considers equivalent)
|
|
|
|
are considered private. Private members are not added to the module's member
|
|
|
|
set, but they are visible from within the module itself. This follows Python's
|
|
|
|
and Dart's privacy models, and bears some similarity to CSS's use of leading
|
|
|
|
hyphens to indicate experimental vendor features.
|
2016-01-09 03:18:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
For backwards-compatibility, privacy does not apply across `@import` boundaries.
|
|
|
|
If one file imports another, either may refer to the other's private members.
|
2016-01-09 03:22:54 +00:00
|
|
|
|
2016-02-06 01:37:08 +00:00
|
|
|
```scss
|
|
|
|
// This function is private and may only be used within this module.
|
|
|
|
@function -parse-gutters($short) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
// By contrast, this mixin is part of the module's public API.
|
|
|
|
@mixin gutters($span) {
|
|
|
|
// But it can use private members within its own module.
|
|
|
|
$span: -parse-gutters($span);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2016-01-09 03:22:54 +00:00
|
|
|
### Resolving Members
|
|
|
|
|
|
|
|
The main function of the module system is to control how [member](#member) names
|
|
|
|
are resolved across files—that is, to find the definition corresponding to a
|
|
|
|
given name. Given a set of [module](#module)s loaded via `@use` and a member
|
|
|
|
type and name to resolve:
|
|
|
|
|
|
|
|
* If the name begins with a module's prefix followed by a hyphen:
|
|
|
|
|
|
|
|
* Strip the prefix and hyphen to get the *unprefixed name*.
|
|
|
|
|
2016-01-23 01:37:01 +00:00
|
|
|
* If the module doesn't have a member of the given type with the unprefixed
|
|
|
|
name, resolution fails.
|
|
|
|
|
|
|
|
* If the module's `@use` directive has a `mixin` clause and the
|
2016-01-30 00:56:37 +00:00
|
|
|
[module mixin](#module-mixins) hasn't yet been included, or has been
|
|
|
|
included more than once, resolution fails.
|
2016-01-09 03:22:54 +00:00
|
|
|
|
2016-01-23 01:37:01 +00:00
|
|
|
* Otherwise, use the module's definition.
|
|
|
|
|
|
|
|
* If the type is "mixin" and the name is exactly a module's prefix, and that
|
|
|
|
module's `@use` directive has a `mixin` clause, use its
|
|
|
|
[module mixin](#module-mixins).
|
2016-01-09 03:22:54 +00:00
|
|
|
|
|
|
|
* If a member of the given type with the given name has already been defined in
|
2016-02-06 00:53:54 +00:00
|
|
|
the current source file or exists in the current
|
|
|
|
[import context](#import-context), use its definition.
|
2016-01-09 03:22:54 +00:00
|
|
|
|
|
|
|
* If such a member is defined later on in the file, resolution fails. This
|
|
|
|
ensures that any change in name resolution caused by reordering a file causes
|
|
|
|
an immediate error rather than an unexpected behavioral change.
|
|
|
|
|
2016-01-22 22:54:20 +00:00
|
|
|
* If such a member is defined in exactly one unprefixed module, use that
|
2016-01-09 03:22:54 +00:00
|
|
|
module's definition.
|
|
|
|
|
2016-01-22 22:54:20 +00:00
|
|
|
* Otherwise, if such a member is defined in more than one unprefixed module,
|
|
|
|
resolution fails. This ensures that, if a new version of a package produces a
|
|
|
|
conflicting name, it causes an immediate error.
|
2016-01-09 03:22:54 +00:00
|
|
|
|
2016-01-22 22:54:20 +00:00
|
|
|
* Otherwise, if such a member isn't defined in any unprefixed module, resolution
|
|
|
|
fails.
|
2016-01-16 00:21:05 +00:00
|
|
|
|
|
|
|
The hyphenated syntax (`namespace-name`) was chosen in preference to other
|
|
|
|
syntaxes (for example `namespace.name`, `namespace::name`, or `namespace|name`)
|
|
|
|
because it's likely to be compatible with existing code that uses manual
|
|
|
|
namespaces, and because it doesn't overlap with plain CSS syntax. This is
|
|
|
|
especially relevant for namespaced placeholder selectors, because most other
|
|
|
|
reasonable characters are already meaningful in selector contexts.
|
|
|
|
|
|
|
|
The downside to hyphens are that they look like normal identifiers, which makes
|
|
|
|
it less locally clear what's a namespace and what's a normal member name. It
|
|
|
|
also allows module prefixes to shadow other members, and introduces the
|
|
|
|
possibility of conflicting prefixes between modules.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
### Using Modules
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
When encountering a `@use` directive without a `mixin` clause, the first step is
|
|
|
|
to [load](#loading-modules) the [module](#module) with the given URI and the
|
|
|
|
empty configuration. Once that's done, the next step is to determine its
|
|
|
|
**prefix**.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
Each module loaded this may have an associated prefix, which is a Sass
|
|
|
|
identifier that's used to identify the module's [member](#member)s within the
|
|
|
|
current file. No two `@use` directives in a given file may share a prefix,
|
|
|
|
although any number may have no prefix. The prefix for a given `@use`
|
|
|
|
directive's module is determined as follows:
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* If the directive has an `as` clause, use that clause's identifier.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* If the directive has a `no-prefix` clause, then it has no prefix.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* If the module's URI doesn't match the regular expression
|
|
|
|
`(.*/)?([^/]+)(\.[^/]*)?`, the `@use` directive is malformed.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* Call the text captured by the second group of the regular expression the
|
|
|
|
*module name*.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* If the module name isn't a Sass identifier, the `@use` directive is malformed.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* If the module name followed by a hyphen is a initial substring of previous
|
|
|
|
`@use` directive's prefix, or if another `@use` directive's prefix followed by
|
|
|
|
a hyphen is an initial substring of the module name, the `@use` directive is
|
|
|
|
malformed.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* Use the module name.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
This proposal follows Python and diverges from Dart in that `@use` imports
|
|
|
|
modules with a prefix by default. This is for two reasons. First, it seems to be
|
|
|
|
the case that language ecosystems with similar module systems either prefix all
|
|
|
|
imports by convention, or prefix almost none. Because Sass is not
|
|
|
|
object-oriented and doesn't have the built-in namespacing that classes provide
|
|
|
|
many other languages, its APIs tend to be much broader at the top level and thus
|
|
|
|
at higher risk for name conflict. Prefixing by default tilts the balance towards
|
|
|
|
always prefixing, which mitigates this risk.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
Second, a default prefix scheme drastically reduces the potential for
|
|
|
|
inconsistency in prefix choice. If the prefix is left entirely up to the user,
|
|
|
|
different people may choose to prefix `strings.scss` as `strings`, `string`,
|
|
|
|
`str`, or `strs`. This taxes the reusability of code and knowledge, and
|
|
|
|
mitigating it is a benefit.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:37:08 +00:00
|
|
|
```scss
|
|
|
|
// This has the default prefix "susy".
|
|
|
|
@use "susy";
|
|
|
|
|
|
|
|
// This has the explicit prefix "bbn".
|
|
|
|
@use "bourbon" as bbn;
|
|
|
|
|
|
|
|
// This has no prefix.
|
|
|
|
@use "compass" no-prefix;
|
|
|
|
|
|
|
|
// Both packages define their own "gutters()" functions. But because the members
|
|
|
|
// are prefixed, there's no conflict and the user can use both at once.
|
|
|
|
#susy {@include susy-gutters()}
|
|
|
|
#bourbon {@include bbn-gutters()}
|
|
|
|
|
|
|
|
// Users can also import without a prefix at all, which lets them use the
|
|
|
|
// original member names.
|
|
|
|
#compass {@include gutters()}
|
|
|
|
```
|
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
### Module Mixins
|
2016-01-29 23:29:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
[Modules](#module) can be encapsulated in mixins by using `@use`'s `mixin`
|
|
|
|
clause. This allows a module's CSS to only be conditionally included in a
|
|
|
|
document, or to be included in a nested context. It also allows the user of the
|
|
|
|
module to configure it by providing default values for variables that the module
|
|
|
|
uses.
|
2016-01-29 23:29:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
When executing a `@use` directive with a `mixin` clause, the directive's module
|
|
|
|
isn't [loaded as normal](#using-modules). Instead a special *module mixin*, with
|
|
|
|
the same name as the directive's prefix, is introduced into the current source
|
|
|
|
file's namespace.
|
2016-01-29 23:29:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
The module mixin's arguments are derived from the module's members (which we can
|
|
|
|
determine without executing the module). For every variable in module that has a
|
|
|
|
`!default` flag, the module mixin has an argument with the same name and a
|
|
|
|
default value of `null`. These arguments are in the order the variables are
|
|
|
|
defined, although users should be strongly encouraged to only pass them by name.
|
2016-01-29 23:29:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
When this mixin is included:
|
2016-01-29 23:29:02 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* Create a configuration whose variable names are the module mixin's argument
|
|
|
|
names. These variable's values are the values of the corresponding arguments.
|
2016-01-16 03:19:04 +00:00
|
|
|
|
2016-02-06 01:15:36 +00:00
|
|
|
* [Load](#loading-modules) the module with the `@use` directive's URI and this
|
|
|
|
configuration.
|
|
|
|
|
|
|
|
* If the current source file contains a `@forward` directive with the same URI
|
|
|
|
as the `@use` directive, [forward](#forwarding-modules) the loaded module with
|
|
|
|
that `@forward` directive.
|
|
|
|
|
|
|
|
* [Resolve extensions](#resolving-extensions) for the loaded module, then emit
|
|
|
|
the resulting CSS to the location of the `@include`.
|
|
|
|
|
|
|
|
There are several important things to note here. First, every time a module
|
|
|
|
mixin is used, its CSS is emitted, which means that the CSS may be emitted
|
|
|
|
multiple times. This behavior makes sense in context, and is unlikely to
|
|
|
|
surprise anyone, but it's good to note nonetheless as an exception to the
|
|
|
|
import-once goal.
|
|
|
|
|
|
|
|
Second, because module mixins' CSS is included directly in another module's,
|
|
|
|
`@use` directives with `mixin` clauses do not create edges on the module graph.
|
|
|
|
Those edges represent a *reference to* another module's CSS, whereas module
|
|
|
|
mixins *directly include* that CSS. Keeping them out of the module graph also
|
|
|
|
allows users to dynamically choose not to include the module at all and avoid
|
|
|
|
using its CSS at all.
|
|
|
|
|
|
|
|
Finally, module mixins don't affect name resolution at all, except in that a
|
|
|
|
name that refers to a member of the module will fail to load until the mixin has
|
|
|
|
been included. The scoping of these names is independent of the location of the
|
|
|
|
module mixin's `@include` directive, so even if it's included in a deeply-nested
|
|
|
|
selector hierarchy its members will be accessible at the root of the document.
|
2016-01-22 23:34:36 +00:00
|
|
|
|
2016-02-06 01:37:08 +00:00
|
|
|
```scss
|
|
|
|
// This defines a mixin named "susy" that loads the module with custom
|
|
|
|
// configuration.
|
|
|
|
@use "susy" mixin;
|
|
|
|
|
|
|
|
// Forward all the members from susy, with our customization included.
|
|
|
|
@forward "susy";
|
|
|
|
|
|
|
|
// These variables are set in the scope of susy's main module.
|
|
|
|
@include susy(
|
|
|
|
$columns: 4,
|
|
|
|
$gutters: 0.25,
|
|
|
|
$math: fluid
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
2016-01-22 23:34:36 +00:00
|
|
|
### Forwarding Modules
|
|
|
|
|
|
|
|
The [`@forward`](#forward) directive forwards another [module](#module)'s public
|
|
|
|
API as though it were part of the current module's. Note that it *does not* make
|
|
|
|
that API available to the current module; that is purely the domain of `@use`.
|
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
First, we define a general procedure for forward a module (call it the
|
|
|
|
*forwarded module*) with a `@forward` directive:
|
2016-01-22 23:34:36 +00:00
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
* For every member (call it the *forwarded member*) in the forwarded module:
|
2016-01-22 23:34:36 +00:00
|
|
|
|
|
|
|
* If there's a member with the same name and type defined later on in the
|
|
|
|
current [source file](#source-file), do nothing. Giving local definitions
|
|
|
|
precedence ensures that a module continues to expose the same API if a
|
|
|
|
forwarded module changes to include a conflicting member.
|
|
|
|
|
|
|
|
* If the `@forward` directive has a `show` clause that doesn't include the
|
|
|
|
forwarded member's name, do nothing.
|
|
|
|
|
|
|
|
* If the `@forward` directive has a `hide` clause that does include the
|
|
|
|
forwarded member's name, do nothing.
|
|
|
|
|
|
|
|
* If another `@forward` directive's module has a member with the same name and
|
|
|
|
type, the directive is malformed. Failing here ensures that, in the absence
|
|
|
|
of an obvious member that takes precedence, conflicts are detected as soon
|
|
|
|
as possible.
|
|
|
|
|
|
|
|
* Otherwise, add the member to the current module's collection of members.
|
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
Note that the procedure defined above is not directly executed when encountering
|
|
|
|
a `@forward` directive. To execute a `@forward` directive:
|
|
|
|
|
2016-02-06 00:52:38 +00:00
|
|
|
* If the current source file contains a `@use` directive with the same URI as
|
|
|
|
the `@forward` directive and a `mixin` clause:
|
2016-01-30 01:11:31 +00:00
|
|
|
|
|
|
|
* If there are multiple `@use` directives with that URI, the `@forward`
|
|
|
|
directive is malformed. This is true regardless of whether the additional
|
|
|
|
`@use` directives have `mixin` declarations.
|
|
|
|
|
|
|
|
* Otherwise, do nothing. The module will be forwarded when the module is
|
|
|
|
included.
|
|
|
|
|
|
|
|
* Otherwise, [load](#loading-modules) the module for the directive's URI with
|
|
|
|
the empty configuration.
|
|
|
|
|
|
|
|
* Forward the loaded module.
|
|
|
|
|
2016-01-22 23:34:36 +00:00
|
|
|
This forwards all members by default to reduce the churn and potential for
|
|
|
|
errors when a new member gets added to a forwarded module. It's likely that most
|
|
|
|
packages will already break up their definitions into many smaller modules which
|
|
|
|
will all be forwarded, which makes the API definition explicit enough without
|
|
|
|
requiring additional explicitness here.
|
2016-01-23 01:37:01 +00:00
|
|
|
|
2016-01-30 01:11:31 +00:00
|
|
|
> **Design note:**
|
|
|
|
>
|
|
|
|
> There should definitely be a way to forward members from a configured module,
|
|
|
|
> but I'm not sure whether this is the best way to do it. It weirds me out that
|
|
|
|
> an identical `@forward` declaration can mean different things based on `@use`
|
|
|
|
> directives around it. But I haven't come up with a better alternative.
|
|
|
|
|
2016-02-06 01:37:08 +00:00
|
|
|
```scss
|
|
|
|
// _susy.scss would forward its component files so users would see its full API
|
|
|
|
// with a single @use, but the definitions don't have to live in a single file.
|
|
|
|
|
|
|
|
@forward "susy/grids";
|
|
|
|
@forward "susy/box-sizing";
|
|
|
|
@forward "susy/content";
|
|
|
|
|
|
|
|
// You can show or hide members that are only meant to be used within the
|
|
|
|
// package. You could also choose not to forward this module at all and only
|
|
|
|
// use it from internal modules.
|
|
|
|
@forward "susy/settings" hide susy-defaults;
|
|
|
|
```
|
|
|
|
|
2016-02-06 00:53:54 +00:00
|
|
|
### Importing Files
|
|
|
|
|
|
|
|
For the duration of the Sass 4.x release cycle, `@use` will coexist with the old
|
|
|
|
`@import` directive in order to ease the burden of migration. This means that we
|
|
|
|
need to define how the two directives interact.
|
|
|
|
|
|
|
|
When executing an `@import` directive:
|
|
|
|
|
|
|
|
* If there is no current [import context](#import-context), create one that
|
|
|
|
contains all of the current [module](#module)'s members that have been defined
|
|
|
|
so far. Note that this does not include members visible because of `@use`, nor
|
|
|
|
does it include members from [forwarded](#forwarding-modules) modules.
|
|
|
|
|
|
|
|
* Look up the [source file](#source-file) with the given URI.
|
|
|
|
|
|
|
|
* If no such file can be found, importing fails.
|
|
|
|
|
|
|
|
* If the source file is currently being executed with the current import
|
|
|
|
context, loading fails.
|
|
|
|
|
|
|
|
* [Execute](#executing-files) that file with an empty configuration and the
|
|
|
|
current import context. Note that this execution can mutate the current import
|
|
|
|
context.
|
|
|
|
|
|
|
|
* Emit the resulting module's CSS to the location of the `@import`.
|
|
|
|
|
|
|
|
* Add the resulting module's extensions to the current module.
|
|
|
|
|
|
|
|
* Add any members of the resulting module that don't conflict with the current
|
|
|
|
import context to that context, and to the current module. This makes
|
|
|
|
forwarded members available in the importing module, but does not them to
|
|
|
|
overwrite existing members with the same names and types.
|
|
|
|
|
|
|
|
When a stylesheet contains only `@import`s without any `@use`s, the `@import`s
|
|
|
|
are intended to work exactly as they did in previous Sass versions. Any
|
|
|
|
difference should be considered a bug in this specification.
|
|
|
|
|
|
|
|
This definition allows files that include `@use` to be imported. Doing so
|
|
|
|
includes those modules' CSS as well as any members they define or forward. This
|
|
|
|
makes it possible for users to continue using `@import` even when their
|
|
|
|
dependencies switch to `@use`, which conversely makes it safer for packages to
|
|
|
|
switch to `@use`.
|
|
|
|
|
|
|
|
It also allows files that use `@import` to be used as modules. Doing so treats
|
|
|
|
them as though all CSS and members were included in the module itself.
|
2016-02-12 20:57:25 +00:00
|
|
|
|
|
|
|
## Built-In Modules
|
|
|
|
|
|
|
|
The new module system provides an opportunity to bring more locality and
|
|
|
|
organization to the set of built-in functions that comprise Sass's core library.
|
|
|
|
These functions currently reside in the same global namespace as everything
|
|
|
|
else, which makes it difficult to add new functions without risking conflict
|
|
|
|
with either user code or future CSS functions (which has
|
|
|
|
[happened in practice][issue-631]).
|
|
|
|
|
|
|
|
[issue 631]: https://github.com/sass/sass/issues/631
|
|
|
|
|
|
|
|
We'll move all current built-in functions to built-in [module](#module)s, except
|
|
|
|
for those functions that are intentionally compatible with plain CSS functions.
|
|
|
|
These modules are identified by URIs that begin with "sass:". This scheme was
|
|
|
|
chosen to avoid conflicting with plausible filenames while still being
|
|
|
|
relatively concise.
|
|
|
|
|
|
|
|
The built-in functions will be organized as follows:
|
|
|
|
|
|
|
|
| Current Name | New Name | Module | | Current Name | New Name | Module |
|
|
|
|
| ------------------------ | ------------| ------------- |---| ------------------------ | ------------| ------------- |
|
|
|
|
| `rgb` | | *global* | | `percentage` | | sass:math |
|
|
|
|
| `rgba` | | *global* | | `round` | | sass:math |
|
|
|
|
| `hsl` | | *global* | | `ceil` | | sass:math |
|
|
|
|
| `hsla` | | *global* | | `floor` | | sass:math |
|
|
|
|
| `if` | | *global* | | `abs` | | sass:math |
|
|
|
|
| | | | | `min` | | sass:math |
|
|
|
|
| `red` | | sass:color | | `max` | | sass:math |
|
|
|
|
| `blue` | | sass:color | | `random` | | sass:math |
|
|
|
|
| `green` | | sass:color | | `unit` | | sass:math |
|
|
|
|
| `mix` | | sass:color | | `unitless` | | sass:math |
|
|
|
|
| `hue` | | sass:color | | `comparable` | | sass:math |
|
|
|
|
| `saturation` | | sass:color | | | | |
|
|
|
|
| `lightness` | | sass:color | | `length` | | sass:list |
|
|
|
|
| `adjust-hue` | | sass:color | | `nth` | | sass:list |
|
|
|
|
| `lighten` | | sass:color | | `set-nth` | | sass:list |
|
|
|
|
| `darken` | | sass:color | | `join` | | sass:list |
|
|
|
|
| `saturate` | | sass:color | | `append` | | sass:list |
|
|
|
|
| `desaturate` | | sass:color | | `zip` | | sass:list |
|
|
|
|
| `grayscale` | | sass:color | | `index` | | sass:list |
|
|
|
|
| `complement` | | sass:color | | `list-separator` | `separator` | sass:list |
|
|
|
|
| `invert` | | sass:color | | | | |
|
|
|
|
| `alpha` | | sass:color | | `feature-exists` | | sass:meta |
|
|
|
|
| `opacify` | | sass:color | | `variable-exists` | | sass:meta |
|
|
|
|
| `transparentize` | | sass:color | | `global-variable-exists` | | sass:meta |
|
|
|
|
| `adjust-color` | `adjust` | sass:color | | `function-exists` | | sass:meta |
|
|
|
|
| `scale-color` | `scale` | sass:color | | `mixin-exists` | | sass:meta |
|
|
|
|
| `change-color` | `change` | sass:color | | `inspect` | | sass:meta |
|
|
|
|
| `ie-hex-str` | | sass:color | | `type-of` | | sass:meta |
|
|
|
|
| | | | | `call` | | sass:meta |
|
|
|
|
| `map-get` | `get` | sass:map | | `unique-id` | | sass:meta |
|
|
|
|
| `map-merge` | `merge` | sass:map | | | | |
|
|
|
|
| `map-remove` | `remove` | sass:map | | `unquote` | | sass:string |
|
|
|
|
| `map-keys` | `keys` | sass:map | | `quote` | | sass:string |
|
|
|
|
| `map-values` | `values` | sass:map | | `str-length` | `length` | sass:string |
|
|
|
|
| `map-has-key` | `has-key` | sass:map | | `str-insert` | `insert` | sass:string |
|
|
|
|
| `keywords` | | sass:map | | `str-index` | `index` | sass:string |
|
|
|
|
| | | | | `str-slice` | `slice` | sass:string |
|
|
|
|
| `selector-nest` | `nest` | sass:selector | | `to-upper-case` | | sass:string |
|
|
|
|
| `selector-append` | `append` | sass:selector | | `to-lower-case` | | sass:string |
|
|
|
|
| `selector-replace` | `replace` | sass:selector | | | | |
|
|
|
|
| `selector-unify` | `unify` | sass:selector | | | | |
|
|
|
|
| `is-superselector` | | sass:selector | | | | |
|
|
|
|
| `simple-selectors` | | sass:selector | | | | |
|
|
|
|
| `selector-parse` | `parse` | sass:selector | | | | |
|
|
|
|
|
|
|
|
> **Design note:**
|
|
|
|
>
|
|
|
|
> For now, I've left in all existing functions. However, given that we'll be
|
|
|
|
> asking users to make such a big transition anyway, it may be worth considering
|
|
|
|
> whether we want to get rid of some. I'm thinking in particular of individual
|
|
|
|
> color functions that are redundant with `adjust-color()`.
|
|
|
|
|
|
|
|
Regardless of what configuration is used to load them, built-in modules will
|
|
|
|
contain only the functions described above. They won't contain any other
|
|
|
|
[member](#member)s, CSS, or extensions. New members may be added in the future,
|
|
|
|
but CSS will not be added to existing modules.
|
|
|
|
|
|
|
|
```scss
|
|
|
|
@use "sass:color";
|
|
|
|
@use "sass:map";
|
|
|
|
@use "sass:math";
|
|
|
|
|
|
|
|
// Adapted from https://css-tricks.com/snippets/sass/luminance-color-function/.
|
|
|
|
@function luminance($color) {
|
|
|
|
$colors: (
|
|
|
|
'red': color-red($color),
|
|
|
|
'green': color-green($color),
|
|
|
|
'blue': color-blue($color)
|
|
|
|
);
|
|
|
|
|
|
|
|
@each $name, $value in $colors {
|
|
|
|
$adjusted: 0;
|
|
|
|
$value: $value / 255;
|
|
|
|
|
|
|
|
@if $value < 0.03928 {
|
|
|
|
$value: $value / 12.92;
|
|
|
|
} @else {
|
|
|
|
$value: ($value + .055) / 1.055;
|
|
|
|
$value: math-pow($value, 2.4);
|
|
|
|
}
|
|
|
|
|
|
|
|
$colors: map-merge($colors, ($name: $value));
|
|
|
|
}
|
|
|
|
|
|
|
|
@return map-get($colors, 'red') * .2126 +
|
|
|
|
map-get($colors, 'green') * .7152 +
|
|
|
|
map-get($colors, 'blue') * .0722;
|
|
|
|
}
|
|
|
|
```
|