# Request For Comments: Module System

Many of the most frequently-requested features for Sass have to do with its imports. The import system that we've had since the very early releases of Sass is, to put it simply, not great. It does little more than textually include one Sass file in another, which makes it hard to keep track of where mixins, functions, and variables were defined and hard to be sure that any new additions won't happen to conflict with something elsewhere in the project. To make matters worse, it overlaps with CSS's built-in @import rule, which forces us to have a bunch of heuristics to decide which is which.

Because of these problems and others, we've wanted to do a full overhaul of the way Sass files relate to one another for a long time. Over the last few years, I've been working with the Sass core team and Sass framework maintainers to create a proposal for a module system that's fit to replace @import. That proposal is now in a place that the core team is pretty happy with, at least as a starting point, so we want to open it up for community feedback.

If you want to read the full proposal, it's available on GitHub. Feel free to file issues for any feedback you have. The main body of the proposal is written as a spec, so it's very detailed, but the Goals, Summary, and FAQ sections (reproduced below) should be accessible to anyone familiar with Sass.

### Goals

#### High-Level

These are the philosophical design goals for the module system as a whole. While they don't uniquely specify a system, they do represent the underlying motivations behind many of the lower-level design decisions.

• 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 users to access or modify. The organization of a library's implementation into files should be flexible enough to change without changing the user-visible API.

• Configuration. Sass is unusual among languages in that its design leads to 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, including computation of other top-level variables' values. The module system should allow the user to flexibly use and configure modules with side-effects.

#### 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.

• Import once. Because @import is a literal textual inclusion, multiple @imports 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 for little benefit, 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.

• 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.

#### Non-Goals

These are potential goals that we have explicitly decided to avoid pursuing as part of this proposal for various reasons. Some of them may be on the table for future work, but we don't consider them to be blocking the module system.

• 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 @extended 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 include in the initial module system.

• Context-independent modules. It's tempting to try to make the loaded form of a module, including the CSS it generates and the resolved values of all its variables, totally independent of the entrypoint that cause it to be loaded. This would make it possible to share loaded modules across multiple compilations and potentially even serialize them to the filesystem for incremental compilation.

However, it's not feasible in practice. Modules that generate CSS almost always do so based on some configuration, which may be changed by different entrypoints rendering caching useless. What's more, multiple modules may depend on the same shared module, and one may modify its configuration before the other uses it. Forbidding this case in general would effectively amount to forbidding modules from generating CSS based on variables.

Fortunately, implementations have a lot of leeway to cache information that the can statically determine to be context-independent, including source trees and potentially even constant-folded variable values and CSS trees. Full context independence isn't likely to provide much value in addition to that.

• Increased strictness. Large teams with many people often want stricter rules around how Sass stylesheets are written, to enforce best practices and quickly catch mistakes. It's tempting to use a new module system as a lever to push strictness further; for example, we could make it harder to have partials directly generate CSS, or we could decline to move functions we'd prefer people avoid to the new built-in modules.

As tempting as it is, though, we want to make all existing use-cases as easy as possible in the new system, even if we think they should be avoided. This module system is already a major departure from the existing behavior, and will require a substantial amount of work from Sass users to support. We want to make this transition as easy as possible, and part of that is avoiding adding any unnecessary hoops users have to jump through to get their existing stylesheets working in the new module system.

Once @use is thoroughly adopted in the ecosystem, we can start thinking about increased strictness in the form of lints or TypeScript-style --strict-* flags.

• Code splitting. The ability to split monolithic CSS into separate chunks that can be served lazily is important for maintaining quick load times for very large applications. However, it's orthogonal to the problems that this module system is trying to solve. This system is primarily concerned with scoping Sass APIs (mixins, functions, and placeholders) rather than declaring dependencies between chunks of generated CSS.

We believe that this module system can work in concert with external code-splitting systems. For example, the module system can be used to load libraries that are used to style individual components, each of which is compiled to its own CSS file. These CSS files could then declare dependencies on one another using special comments or custom at-rules and be stitched together by a code-splitting post-processor.

### Summary

This proposal adds two at-rules, @use and @forward, which may only appear at the top level of stylesheets before any rules (other than @charset). Together, they're intended to completely replace @import, which will eventually be deprecated and even more eventually removed from the language.

#### @use

@use makes CSS, variables, mixins, and functions from another stylesheet accessible in the current stylesheet. By default, variables, mixins, and functions are available in a namespace based on the basename of the URL.

In addition to namespacing, there are a few important differences between @use and @import:

• @use only executes a stylesheet and includes its CSS once, no matter how many times that stylesheet is used.
• @use only makes names available in the current stylesheet, as opposed to globally.
• Members whose names begin with - or _ are private to the current stylesheet with @use.
• If a stylesheet includes @extend, that extension is only applied to stylesheets it imports, not stylesheets that import it.

Note that placeholder selectors are not namespaced, but they do respect privacy.

##### Controlling Namespaces

Although a @use rule's default namespace is determined by the basename of its URL, it can also be set explicitly using as.

The special construct as * can also be used to include everything in the top-level namespace. Note that if multiple modules expose members with the same name and are used with as *, Sass will produce an error.

##### Configuring Libraries

With @import, libraries are often configured by setting global variables that override !default variables defined by those libraries. Because variables are no longer global with @use, it supports a more explicit way of configuring libraries: the with clause.