Skip to content

Latest commit

 

History

History
509 lines (333 loc) · 17.9 KB

modules.md

File metadata and controls

509 lines (333 loc) · 17.9 KB

Modules

Table of Contents

Definitions

Member

A member is a Sass construct that's defined either by the user or the implementation and is identified by a Sass identifier. This currently includes variables, mixins, and functions (but not placeholder selectors). All members have definitions associated with them, whose specific structure depends on the type of the given member.

Two members are considered identical if they have the same name, type, source location, and were defined in or forwarded from the same original module.

Each member type has its own namespace in Sass, so for example the mixin name doesn't conflict with the function name or the variable $name.

CSS Tree

A CSS tree is an abstract CSS syntax tree. It has multiple top-level CSS statements like at-rules or style rules. The ordering of these statements is significant. A CSS tree cannot contain any Sass-specific constructs, with the notable exception of placeholder selectors.

An empty CSS tree contains no statements.

Configuration

A configuration is a map from variable names to SassScript values and an opaque ID. An empty configuration contains no entries.

A new configuration ID is unique unless otherwise specified.

Module

A module is a collection of various properties:

  • A set of members that contains at most one member of any given type and name.

    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 different names.

    The names (and mixin and function signatures) of a module's members are static, and can be determined without executing its associated source file. This means that any possible module for a given source file has the same member names and signatures regardless of the context in which those modules are loaded.

  • A set of extensions.

  • A CSS tree.

    This tree is empty for built-in modules and user-defined modules that only define variables, functions, and mixins without including any plain CSS rules.

  • A list of references to other modules, known as the module's dependencies, in the same order as their @use rules and/or @forward rules appear in the module's source file. If a dependency is referred to from multiple rules, its order is determined by the first such rule.

    Modules without a source file never have dependencies. Each dependency is guaranteed to correspond to at least one @use rule or @forward rule.

  • An optional source file.

    Note that built-in modules do not have source files associated with them.

  • An absolute URL, known as the module's canonical URL. If the module has a source file, this must be the same as the source file's canonical URL.

Once a user-defined module has been returned by Executing a File, it is immutable except for its variable values. Built-in modules are always immutable.

Module Graph

The set of modules loaded in the course of processing a stylesheet can be construed as a directed acyclic graph where the vertices are modules and the edges are @use rules and/or @forward rules. We call this the module graph.

The module graph is not allowed to contain cycles because they make it impossible to guarantee that all dependencies of a module are available before that module is loaded. Although the names and APIs of a dependency's members can be determined without executing it, Sass allows code to be executed during load, so those members may not behave correctly when invoked before the dependency is executed.

Import Context

An import context is a set of members that contains at most one member of any given type and name. It's always mutable.

Import contexts serve as glue between the old @import rule and the module system. It serves as a shared global namespace for stylesheets loaded using @import rules, while also preventing global names from leaking into or out of stylesheets loaded using @use rules and/or @forward rules.

Built-In Module

A built-in module is a module defined either by the Sass specification or by the host environment of the Sass compilation in some implementation-specific way. Modules defined by the Sass specification all have the scheme sass: and are all described in the built-in-modules directory. Modules defined outside the Sass compilation may not use the scheme sass:.

Built-in modules may contain mixins, variables, or functions, but they may never contain CSS or extensions.

Importer

An importer is a function that takes a string that may be either a relative or absolute URL and returns three values: a string (the text of a stylesheet), a syntax ("indented", "scss", or "css"), and an absolute URL (that stylesheet's canonical URL). It may also return null to indicate that the importer doesn't recognize the URL in question or cannot find a corresponding stylesheet. If the URL is recognized but invalid, it should throw an error rather than returning null. What constitutes "recognized" or "invalid" is left up to the importer.

The details of an importer's behavior is typically defined by the end user in an implementation-specific way. However, all importers must adhere to the following contract:

  • When the URL returned by an importer is passed back to that importer, it must return the same result.

  • The importer must return the same result for all URLs that refer to the same file, although what specifically constitutes "the same file" is left up to the importer.

Importers are represented as a single function in the spec to simplify the writing of algorithms, but implementations are encouraged to have users instead define two separate functions: a canonicalize() function that converts an input string into a canonical URL, and a load() function that loads the contents of a canonical URL. This allows implementations to avoid the overhead of reloading the same file over and over.

Filesystem Importer

A filesystem importer is an importer with an associated absolute file: URL named base. When a filesystem importer is invoked with a string named string:

  • Let url be the result of parsing string as a URL with base as the base URL. If this returns a failure, throw that failure.

  • If url's scheme is not file, return null.

  • Let resolved be the result of resolving url.

  • If resolved is null, return null.

  • Let text be the contents of the file at resolved.

  • Let syntax be:

    • "scss" if url ends in .scss.
    • "indented" if url ends in .sass.
    • "css" if url ends in .css.

    The algorithm for resolving a file: URL guarantees that url will have one of these extensions.

  • Return text, syntax, and resolved.

Global Importer List

The global importer list is a list of importers that's set for the entire duration of a Sass compilation.

Basename

The basename of a URL is the final component of that URL's path.

Dirname

The dirname of a URL is the prefix of that URL up to, but not including, the beginning of its basename.

Syntax

The module system defines the following syntax for referring to names from other modules:

PublicIdentifier     ::= <ident-token> that doesn't begin with '-' or '_'
NamespacedIdentifier ::= <ident-token> | <ident-token> '.' PublicIdentifier

No whitespace is allowed before or after the '.' in NamespacedIdentifier.

Procedures

Loading a Module

This algorithm takes a string argument and configuration config and returns a module:

  • If argument is a valid URL with scheme sass:

    • If config is not empty, throw an error.

    • If a built-in module exists with the exact given URL, return it.

    • Otherwise, throw an error.

  • Let file be the result of loading the file at argument.

  • If file is null, throw an error.

  • If file has already been executed by the Loading a Module procedure:

    • If config is not empty and has a different ID than the configuration that was passed the first time file was executed by the Loading a Module procedure, throw an error.

      An ID may be reused in a new configuration via @forward ... with.

    • Otherwise, return the module that execution produced.

  • If file is currently being executed, throw an error.

    This disallows circular @uses, which ensures that modules can't be used until they're fully initialized.

  • Otherwise, return the result of executing file with config and a new import context.

    For simplicity, the spec creates an import context for every module. Implementations are encouraged to avoid eagerly allocating resources for imports, though, to make use-cases only involving @use more efficient.

Loading a Source File

This algorithm takes a string, argument, and returns either a source file or null.

  • If argument is a relative URL:

    • Let resolved be the result of parsing argument as a URL with the current source file's canonical URL as the base URL.

    • Let result be the result of passing resolved to the current source file's importer.

    • If result is not null:

      • Let ast be the result of parsing result's text as result's syntax.

      • Return a source file with ast as its abstract syntax tree, result's URL as its canonical URL, and the current source file's importer as its importer.

  • For each importer in the global importer list:

    • Let result be the result of passing argument to importer.

    • If result is not null:

      • Let ast be the result of parsing result's text as result's syntax.

      • Return a source file with ast as its abstract syntax tree, result's URL as its canonical URL, and importer as its importer.

  • Return null.

Resolving a file: URL

This algorithm takes a URL, url, whose scheme must be file and returns either another URL that's guaranteed to point to a file on disk or null.

Resolving a file: URL for Extensions

This algorithm takes a URL, url, whose scheme must be file and returns either another URL that's guaranteed to point to a file on disk or null.

Resolving a file: URL for Partials

This algorithm takes a URL, url, whose scheme must be file and returns either another URL that's guaranteed to point to a file on disk or null.

  • If url's basename begins with "_":

    • If a file exists on disk at url, return url.

      Otherwise return null.

  • Let partial be dirname(url) + "_" + basename(url).

  • If a file exists on disk at both url and partial, throw an error.

  • If a file exists on disk at url, return url.

  • If a file exists on disk at partial, return partial.

  • Return null.

Resolving a Member

This algorithm takes a member name name and a member type type, and returns a member of type type or null.

  • If name is a plain Identifier or a Variable that's not a NamespacedVariable:

    • Let scope be the scope of the innermost block containing the current statement such that scope has a member of type type named name, or null if no such scope exists.

    • If scope is not null, return scope's value of type type named name.

  • If name is a NamespacedIdentifier of the form namespace.raw-name or a Variable of the form namespace.$raw-name:

    • Let use be the @use rule in the current source file whose namespace is namespace. If there isn't exactly one such rule, throw an error.

      Unlike other identifiers in Sass, module namespaces do not treat - and _ as equivalent.

    • If use hasn't been executed yet, throw an error.

    • Otherwise, let module be use's module.

    • Return the member of module with type type and name raw-name. If there is no such member, throw an error.

  • If type is not "variable" and the current source file contains a top-level definition of a member of type type named name:

    Local function and mixin definitions shadow those from global @use rules, so that an upstream package adding a member is less likely to break its downstream dependencies. We exclude variables from this because a top-level variable definition will set the module's variable value rather than defining a new variable local to this module.

    • If the current import context contains a member member of type type named name, return it.

      This includes member definitions within the current module.

    • Otherwise, return null.

      It's an error to refer to a local member before it's defined, even if a member with the same name is defined in a loaded module. The referent to a member is guaranteed not to change due to definitions later in the file.

  • Let members be the set of unique members of type type named name in modules of the global @use rules.

  • If the current import context contains a member member of type type named name:

    • If members is not empty, throw an error.

    • Otherwise, return member.

  • Otherwise, if members contains more than one member, throw an error.

    This ensures that, if a new version of a library produces a conflicting name, it causes an immediate error.

  • Otherwise, if modules contains a single module, return the member of type type named name in that module.

  • Otherwise, if the implementation defines a global member member of type type named name, return that member.

    This includes the global functions and mixins defined as part of the Sass spec, and may also include other members defined through the implementation's host language API.

  • Otherwise, return null.