Skip to content

Commit

Permalink
Document the module system in existing files
Browse files Browse the repository at this point in the history
Documentation for @use and @forward themselves will come in a future
PR.

Partially addresses #263
  • Loading branch information
nex3 committed Aug 21, 2019
1 parent 835ff5a commit 9bd1148
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 87 deletions.
2 changes: 2 additions & 0 deletions data/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ toc:
- Interpolation: /documentation/interpolation
- At-Rules: /documentation/at-rules
:children:
- <code>@use</code>: /documentation/at-rules/use
- <code>@forward</code>: /documentation/at-rules/forward
- <code>@import</code>: /documentation/at-rules/import
- <code>@mixin</code> and <code>@include</code>: /documentation/at-rules/mixin
- <code>@function</code>: /documentation/at-rules/function
Expand Down
128 changes: 128 additions & 0 deletions source/documentation/at-rules/import.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,48 @@ required to have quotes.

[indented syntax]: ../syntax#the-indented-syntax

<% heads_up do %>
Continued use of the `@import` rule is discouraged. It will be [gradually
phased out][] over the next few years, and eventually removed from the
language entirely. The [`@use` rule][] should be used instead.

[gradually phased out]: https://github.com/sass/sass/blob/master/accepted/module-system.md#timeline
[`@use` rule]: use

#### What's Wrong With `@import`?

The `@import` rule has a number of serious issues:

* `@import` makes all variables, mixins, and functions globally accessible.
This makes it very difficult for people (or tools) to tell where anything is
defined.

* Because everything's global, libraries are forced to add prefixes to all
their members to avoid naming conflicts.

* [`@extend` rules][] are also global, which makes it difficult to predict
which style rules will be extended.

[`@extend` rules]: extend

* Each stylesheet is executed and its CSS emitted *every time* it's
`@import`ed, which increases compilation time and produces bloated output.

* There was no way to define private members or placeholder selectors that
were inaccessible to downstream stylesheets.

All of these are addressed in `@use` and the new module system.

#### How Do I Migrate?

We've written a [migration tool][] that can automatically convert most
`@import`-based code to `@use`-based code in a flash. Just point it at your
entrypoints and let it run!

[migration tool]: cli/migrator

<% end %>
<% example do %>
// foundation/_code.scss
code {
Expand Down Expand Up @@ -414,3 +456,89 @@ for example based on mixin parameters.

@include google-font("Droid Sans")
<% end %>

## Import and Modules

<%= partial '../snippets/module-system-status' %>

Sass's [module system][] integrates seamlessly with `@import`, whether you're
importing a file that contains `@use` rules or loading a file that contains
imports as a module. We want to make the transition from `@import` to `@use` as
smooth as possible.

[module system]: use

### Importing a Module-System File

When you import a file that contains `@use` rules, the importing file has access
to all members (even private members) defined directly in that file, but *not*
any members from modules that file has loaded. However, if that file contains
[`@forward` rules][], the importing file will have access to forwarded members.
This means that you can import a library that was written to be used with the
module system.

[`@forward` rules]: forward

<% heads_up do %>
When a file with `@use` rules is imported, all the CSS transitively loaded by
those is included in the resulting stylesheet, even if it's already been
included by another import. If you're not careful, this can result in bloated
CSS output!
<% end %>

#### Import-Only Files

An API that makes sense for `@use` might not make sense for `@import`. For
example, `@use` adds a namespace to all members by default so you can safely use
short names, but `@import` doesn't so you might need something longer. If you're
a library author, you may be concerned that if you update your library to use
the new module system, your existing `@import`-based users will break.

To make this easier, Sass also supports *import-only files*. If you name a file
`<name>.import.scss`, it will only be loaded for imports, not for `@use`s. This
way, you can retain compatibility for `@import` users while still providing a
nice API for users of the new module system.

<% example(autogen_css: false) do %>
// _reset.scss

// Module system users write `@include reset.list()`.
@mixin list() {
ul {
margin: 0;
padding: 0;
list-style: none;
}
}
---
// _reset.import.scss

// Legacy import users can keep writing `@include reset-list()`.
@forward "reset" as reset-*;
===
// _reset.sass

// Module system users write `@include reset.list()`.
@mixin list()
ul
margin: 0
padding: 0
list-style: none
---
// _reset.import.sass

// Legacy import users can keep writing `@include reset-list()`.
@forward "reset" as reset-*
<% end %>

### Loading a Module That Contains Imports

When you use `@use` (or `@forward`) load a module that uses `@import`, that
module will contain all the public members defined by the stylesheet you load
*and* everything that stylesheet transitively imports. In other words,
everything that's imported is treated as though it were written in one big
stylesheet.

This makes it easy to convert start using `@use` in a stylesheet even before all
the libraries you depend on have converted to the new module system. Be aware,
though, that if they do convert their APIs may well change!
12 changes: 6 additions & 6 deletions source/documentation/cli/dart-sass.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ The `--stdin` flag may not be used with [many-to-many mode][].

This flag tells Sass to parse the input file as the [indented syntax][]. If it's
used in [many-to-many mode][], all input files are parsed as the indented
syntax, although files they [import][] will have their syntax determined as
usual. The inverse, `--no-indented`, can be used to force all input files to be
parsed as [SCSS][] instead.
syntax, although files they [use][] will have their syntax determined as usual.
The inverse, `--no-indented`, can be used to force all input files to be parsed
as [SCSS][] instead.

[import]: ../at-rules/import
[use]: ../at-rules/use

The `--indented` flag is mostly useful when the input file is coming from
[standard input][], so its syntax can't be automatically determined.
Expand All @@ -110,10 +110,10 @@ h1 {
#### `--load-path`

This option (abbreviated `-I`) adds an additional [load path][] for Sass to look
for imports. It can be passed multiple times to provide multiple load paths.
for stylesheets. It can be passed multiple times to provide multiple load paths.
Earlier load paths will take precedence over later ones.

[load path]: ../at-rules/import#load-paths
[load path]: ../at-rules/use#load-paths

```shellsession
$ sass --load-path=node_modules/bootstrap/dist/css style.scss style.css
Expand Down
6 changes: 3 additions & 3 deletions source/documentation/cli/ruby-sass.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ When compiling whole directories, Sass will ignore [partial files][] whose names
begin with `_`. You can use partials to separate out your stylesheets without
creating a bunch of unnecessary output files.

[partial files]: ../at-rules/import#partials
[partial files]: ../at-rules/use#partials

Many-to-many mode will only compile stylesheets whose dependencies have been
modified more recently than the corresponding CSS file was generated. It will
Expand All @@ -93,10 +93,10 @@ also print status messages when updating stylesheets.
#### `--load-path`

This option (abbreviated `-I`) adds an additional [load path][] for Sass to look
for imports. It can be passed multiple times to provide multiple load paths.
for stylesheets. It can be passed multiple times to provide multiple load paths.
Earlier load paths will take precedence over later ones.

[load path]: ../at-rules/import#load-paths
[load path]: ../at-rules/use#load-paths

```shellsession
$ sass --load-path=node_modules/bootstrap/dist/css style.scss style.css
Expand Down
34 changes: 18 additions & 16 deletions source/documentation/js-api.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ console.log(result.map.toString());

An array of the absolute paths of all Sass files loaded during compilation. If a
stylesheet was loaded from an [importer][] that returned the stylesheet's
contents, the raw strings of that stylesheet's `@import` is included in this
array.
contents, the raw string of the `@use` or `@import` that loaded that stylesheet
is included in this array.

[importer]: #importer

Expand Down Expand Up @@ -188,9 +188,9 @@ provides the same information.
The stylesheet where the error occurred. If the error occurred in a stylesheet
loaded from disk, this is the absolute path of that stylesheet. If the error
occurred in a stylesheet that was loaded from an [importer][] which returned the
stylesheet's contents, this is the raw string of that stylesheet's `@import`. If
it occurred in the contents of the [`data` option][], this is the string
`"stdin"`.
stylesheet's contents, this is the raw string of the `@use` or `@import` that
loaded that stylesheet. If it occurred in the contents of the [`data` option][],
this is the string `"stdin"`.

#### `error.line`

Expand Down Expand Up @@ -741,21 +741,23 @@ h1 {
somewhere else to break it.
<% end %>

This option defines one or more additional handlers for loading files when an
[`@import` rule][] is encountered. It can either be a single JavaScript
function, or an array of functions. These functions are always passed two
arguments:
This option defines one or more additional handlers for loading files when a
[`@use` rule] or an [`@import` rule][] is encountered. It can either be a single
JavaScript function, or an array of functions. These functions are always passed
two arguments:

[`@use` rule]: at-rules/use
[`@import` rule]: at-rules/import

1. The `@import` rule's URL as a string, exactly as it appears in the
1. The `@use` or `@import` rule's URL as a string, exactly as it appears in the
stylesheet.
2. A string identifying for the stylesheet that contained the `@import`. This
identifier's format depends on how that stylesheet was loaded:
2. A string identifying for the stylesheet that contained the `@use` or
`@import`. This identifier's format depends on how that stylesheet was
loaded:
* If the stylesheet was loaded from the filesystem, it's the absolute path of
its file.
* If the stylesheet was loaded from an importer that returned its contents,
it's the URL of the `@import` rule that loaded it.
it's the URL of the `@use` or `@import` rule that loaded it.
* If the stylesheet came from the [`data` option][], it's the string
`"stdin"`.

Expand All @@ -775,7 +777,7 @@ asynchronously pass the result of the import once it's complete.

Imports are resolved by trying, in order:

* Loading a file relative to the file in which the `@import` appeared.
* Loading a file relative to the file in which the `@use` or `@import` appeared.

* Each custom importer.

Expand All @@ -793,7 +795,7 @@ sass.render({
// This importer uses the synchronous API, and can be passed to either
// renderSync() or render().
function(url, prev) {
// This generates a stylesheet from scratch for `@import "big-headers"`.
// This generates a stylesheet from scratch for `@use "big-headers"`.
if (url != "big-headers") return null;

return {
Expand All @@ -807,7 +809,7 @@ h1 {
// This importer uses the asynchronous API, and can only be passed to
// render().
function(url, prev, done) {
// Convert `@import "foo/bar"` to "node_modules/foo/sass/bar".
// Convert `@use "foo/bar"` to "node_modules/foo/sass/bar".
var components = url.split('/');
var innerPath = components.slice(1).join('/');
done({
Expand Down
6 changes: 6 additions & 0 deletions source/documentation/snippets/_module-system-status.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% impl_status dart: '(unreleased)', libsass: false, ruby: false do %>
Only Dart Sass currently supports `@use`. Users of other implementations must
use the [`@import` rule][] instead.

[`@import` rule]: /documentation/at-rules/import
<% end %>
2 changes: 1 addition & 1 deletion source/documentation/syntax.html.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Syntax
introduction: >
Sass supports two different syntaxes. Each one can import the other, so it's
Sass supports two different syntaxes. Each one can load the other, so it's
up to you and your team which one to choose.
---

Expand Down
1 change: 1 addition & 0 deletions source/documentation/syntax/structure.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ These statements produce CSS. They can be used anywhere except within a
These statements can only be used at the top level of a stylesheet, or nested
within a CSS statement at the top level:

* [Module loads](../at-rules/use), using `@use`.
* [Imports](../at-rules/import), using `@import`.
* [Mixin definitions](../at-rules/mixin) using `@mixin`.
* [Function definitions](../at-rules/function) using `@function`.
Expand Down
39 changes: 25 additions & 14 deletions source/documentation/variables.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,25 @@ use them to generate CSS.

To make this possible, Sass provides the `!default` flag. This assigns a value
to a variable *only if* that variable isn't defined or its value is [`null`][].
Otherwise, the existing value will be used. This way, users can set variables
before they import your library to customize its behavior.
Otherwise, the existing value will be used.

[`null`]: values/null

### Customizing Modules

<%= partial 'snippets/module-system-status' %>

Variables defined with `!default` can be customized when loading a module with
the [`@use` rule][]. Sass libraries often use `!default` variables to allow
their users to customize the library's CSS.

[`@use` rule]: at-rules/use

To load a module with customization, write `@use <url> with (<variable>:
<value>, <variable>: <value>)`. The customized values will override the
variables' default values. Only variables written at the top level of the
stylesheet with a `!default` flag can be customized.

<% example do %>
// _library.scss
$black: #000 !default;
Expand All @@ -105,10 +119,10 @@ before they import your library to customize its behavior.
}
---
// style.scss
$black: #222;
$border-radius: 0.1rem;

@import 'library';
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);
===
// _library.sass
$black: #000 !default
Expand All @@ -120,10 +134,7 @@ before they import your library to customize its behavior.
box-shadow: $box-shadow
---
// style.sass
$black: #222
$border-radius: 0.1rem

@import 'library'
@use 'library' with ($black: #222, $border-radius: 0.1rem)
===
code {
border-radius: 0.1rem;
Expand All @@ -134,10 +145,10 @@ before they import your library to customize its behavior.
## Scope

Variables declared at the top level of a stylesheet are *global*. This means
that they can be accessed anywhere after they've been declared—even in another
stylesheet! But that's not true for all variables. Those declared in blocks
(curly braces in SCSS or indented code in Sass) are usually *local*, and can
only be accessed within the block they were declared.
that they can be accessed anywhere in their module after they've been declared.
But that's not true for all variables. Those declared in blocks (curly braces in
SCSS or indented code in Sass) are usually *local*, and can only be accessed
within the block they were declared.

<% example do %>
$global-variable: global value;
Expand Down
Loading

0 comments on commit 9bd1148

Please sign in to comment.