mirror of
https://github.com/sass/sass.git
synced 2024-09-21 10:37:22 +00:00
Specify the way importers integrate into loading source files (#3080)
Paves the way for #2509 Co-authored-by: Awjin Ahn <awjin@google.com>
This commit is contained in:
parent
03ee6aa95b
commit
3b3e27756b
@ -1,3 +1,7 @@
|
||||
## Draft 2.1
|
||||
|
||||
* Minor adjustments to link up with updates in the main spec.
|
||||
|
||||
## Draft 2
|
||||
|
||||
* Rename `CompileResult.includedUrls` to `CompileResult.loadedUrls`. This is
|
||||
|
8
proposal/new-js-api.d.ts
vendored
8
proposal/new-js-api.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* # New JavaScript API: Draft 2
|
||||
* # New JavaScript API: Draft 2.1
|
||||
*
|
||||
* *([Issue](https://github.com/sass/sass/issues/3056))*
|
||||
*
|
||||
@ -204,8 +204,8 @@ export interface CompileResult {
|
||||
* Compiles the Sass file at `path`.
|
||||
*
|
||||
* To perform the compilation, the compiler executes the [compiling a path]
|
||||
* procedure on `path`, with `options.loadPaths` as the import paths. The
|
||||
* compiler must respect the configuration specified by the `options` object.
|
||||
* procedure on `path`, with `options.loadPaths` as `load-paths`. The compiler
|
||||
* must respect the configuration specified by the `options` object.
|
||||
*
|
||||
* [compiling a path]: ../spec/spec.md#compiling-a-path
|
||||
*
|
||||
@ -247,7 +247,7 @@ export function compileAsync(
|
||||
*
|
||||
* To perform the compilation, the compiler executes the [compiling a string]
|
||||
* procedure as follows:
|
||||
* - Use `options.loadPaths` as the import paths.
|
||||
* - Use `options.loadPaths` as `load-paths`.
|
||||
* - Use `options.source` as `string`.
|
||||
* - Use `options.syntax` as `syntax`.
|
||||
* - If `options.syntax` == 'sass', use 'indented'.
|
||||
|
147
spec/modules.md
147
spec/modules.md
@ -10,6 +10,9 @@
|
||||
* [Module Graph](#module-graph)
|
||||
* [Import Context](#import-context)
|
||||
* [Built-In Module](#built-in-module)
|
||||
* [Importer](#importer)
|
||||
* [Filesystem Importer](#filesystem-importer)
|
||||
* [Global Importer List](#global-importer-list)
|
||||
* [Basename](#basename)
|
||||
* [Dirname](#dirname)
|
||||
* [Syntax](#syntax)
|
||||
@ -17,6 +20,7 @@
|
||||
* [Loading a Module](#loading-a-module)
|
||||
* [Loading a Source File](#loading-a-source-file)
|
||||
* [Resolving a `file:` URL](#resolving-a-file-url)
|
||||
* [Resolving a `file:` URL for Extensions](#resolving-a-file-url-for-extensions)
|
||||
* [Resolving a `file:` URL for Partials](#resolving-a-file-url-for-partials)
|
||||
* [Resolving a Member](#resolving-a-member)
|
||||
|
||||
@ -147,6 +151,69 @@ 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](#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][parsing 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`](#resolving-a-file-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](#resolving-a-file-url)
|
||||
> guarantees that `url` will have one of these extensions.
|
||||
|
||||
* Return `text`, `syntax`, and `resolved`.
|
||||
|
||||
[parsing a URL]: https://url.spec.whatwg.org/#concept-url-parser
|
||||
|
||||
### 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.
|
||||
@ -213,66 +280,64 @@ This algorithm takes a string `argument` and [configuration](#configuration)
|
||||
|
||||
### Loading a Source File
|
||||
|
||||
This algorithm takes a string, `argument`, and returns either a [source file][]
|
||||
or null.
|
||||
This algorithm takes a string, `argument`, and returns either a [source file] or
|
||||
null.
|
||||
|
||||
* If the scheme of the [current source file][]'s canonical URL is `file`:
|
||||
* If `argument` is a relative URL:
|
||||
|
||||
[current source file]: spec.md#current-source-file
|
||||
* Let `resolved` be the result of [parsing `argument` as a URL][parsing a URL]
|
||||
with the [current source file]'s canonical URL as the base URL.
|
||||
|
||||
* Let `root` be the current source file's canonical URL.
|
||||
* Let `result` be the result of passing `resolved` to the current source
|
||||
file's [importer](#importer).
|
||||
|
||||
* Otherwise, let `root` be `null`.
|
||||
* If `result` is not null:
|
||||
|
||||
* Let `bases` be a list beginning with `root` if it's non-null, followed by the
|
||||
absolute `file:` URLs of all import paths.
|
||||
* Let `ast` be the result of [parsing] `result`'s text as `result`'s syntax.
|
||||
|
||||
* For each `base` in `bases`:
|
||||
* 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.
|
||||
|
||||
* Let `url` be the result of [parsing `argument` as a URL][] with `base` as
|
||||
the base URL.
|
||||
* For each `importer` in the [global importer list](#global-importer-list):
|
||||
|
||||
If this returns a failure, throw that failure.
|
||||
* Let `result` be the result of passing `argument` to `importer`.
|
||||
|
||||
* If `url`'s scheme is not `file`, return null.
|
||||
* If `result` is not null:
|
||||
|
||||
* Let `resolved` be the result of [resolving `url`](#resolving-a-file-url).
|
||||
* Let `ast` be the result of [parsing] `result`'s text as `result`'s syntax.
|
||||
|
||||
* If `resolved` is null:
|
||||
|
||||
* Let `index` be [`dirname(url)`](#dirname) + `"index/"` +
|
||||
[`basename(url)`](#basename).
|
||||
|
||||
* Set `resolved` to the result of [resolving
|
||||
`index`](#resolving-a-file-url).
|
||||
|
||||
* If `resolved` is still null, continue to the next loop.
|
||||
|
||||
* Let `text` be the contents of the file at `resolved`.
|
||||
|
||||
* Let `ast` be:
|
||||
|
||||
* The result of parsing `text` as SCSS if `resolved` ends in `.scss`.
|
||||
* The result of parsing `text` as the indented syntax if `resolved` ends in
|
||||
`.sass`.
|
||||
* The result of [parsing `text` as CSS][] if `resolved` ends in `.css`.
|
||||
|
||||
> The algorithm for [resolving a `file:` URL](#resolving-a-file-url)
|
||||
> guarantees that `resolved` will have one of these extensions.
|
||||
|
||||
* Return a source file with `ast` as its abstract syntax tree and `resolved`
|
||||
as its canonical URL.
|
||||
|
||||
[parsing `argument` as a URL]: https://url.spec.whatwg.org/#concept-url-parser
|
||||
[parsing `text` as CSS]: syntax.md#parsing-text-as-css
|
||||
* 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.
|
||||
|
||||
[current source file]: spec.md#current-source-file
|
||||
[parsing]: syntax.md#parsing-text
|
||||
|
||||
### 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.
|
||||
|
||||
* Let `resolved` be the result of [resolving `url` for extensions][resolving for
|
||||
extensions].
|
||||
|
||||
* If `resolved` is not null, return it. Otherwise:
|
||||
|
||||
* Let `index` be [`dirname(url)`](#dirname) + `"index/"` +
|
||||
[`basename(url)`](#basename).
|
||||
|
||||
* Return the result of [resolving `index` for extensions][resolving for
|
||||
extensions].
|
||||
|
||||
[resolving for extensions]: #resolving-a-file-url-for-extensions
|
||||
|
||||
### 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.
|
||||
|
||||
* If `url` ends in `.scss`, `.sass`, or `.css`:
|
||||
|
||||
* If this algorithm is being run for an `@import`:
|
||||
|
60
spec/spec.md
60
spec/spec.md
@ -71,7 +71,9 @@ invocation of [Executing a File](#executing-a-file).
|
||||
> This an entrypoint to the specification; it's up to each implementation how it
|
||||
> exposes this to the user.
|
||||
|
||||
This algorithm takes a local filesystem path `path` and returns a string.
|
||||
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.
|
||||
|
||||
* Let `text` be the result of decoding the binary contents of the file at
|
||||
`path`.
|
||||
@ -84,35 +86,57 @@ This algorithm takes a local filesystem path `path` and returns a string.
|
||||
|
||||
* Let `url` be the absolute `file:` URL corresponding to `path`.
|
||||
|
||||
* Return the result of [compiling](#compiling-a-string) `text` with `syntax` and
|
||||
`url`.
|
||||
* 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
|
||||
|
||||
### Compiling a String
|
||||
|
||||
> This an entrypoint to the specification; it's up to each implementation how it
|
||||
> exposes this to the user.
|
||||
|
||||
This algorithm takes a string `string`, a syntax `syntax` ("indented", "scss",
|
||||
or "css"), and an optional URL `url`,
|
||||
This algorithm takes:
|
||||
* 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`.
|
||||
|
||||
* Let `ast` be:
|
||||
[importer]: modules.md#importer
|
||||
|
||||
* The result of parsing `text` as the indented syntax if `syntax` is
|
||||
"indented".
|
||||
It runs as follows:
|
||||
|
||||
* The result of [parsing `text` as CSS][] if `syntax` is "css".
|
||||
* Set the [global importer list] to `importers`.
|
||||
|
||||
* The result of parsing `text` as SCSS otherwise.
|
||||
* For each `path` in `load-paths`:
|
||||
|
||||
[parsing `text` as CSS]: syntax.md#parsing-text-as-css
|
||||
* Let `base` be the absolute `file:` URL that refers to `path`.
|
||||
|
||||
* If `url` is null, set it to a unique value.
|
||||
* Add a [filesystem importer] with base `base` to the global importer list.
|
||||
|
||||
> 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.
|
||||
* Let `ast` be the result of [parsing] `text` as `syntax`.
|
||||
|
||||
* Let `file` be the [source file][] with `ast` and canonical URL `url`.
|
||||
* If `url` is null:
|
||||
|
||||
* If `importer` is not null, throw an error.
|
||||
|
||||
* 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.
|
||||
|
||||
* If `importer` is null, set it to a function that always returns null.
|
||||
|
||||
* Let `file` be the [source file][] with `ast`, canonical URL `url`, and
|
||||
importer `importer`.
|
||||
|
||||
[source file]: syntax.md#source-file
|
||||
|
||||
@ -124,6 +148,10 @@ or "css"), and an optional URL `url`,
|
||||
|
||||
* Return the result of converting `css` to a CSS string.
|
||||
|
||||
[filesystem importer]: modules.md#filesystem-importer
|
||||
[parsing]: syntax.md#parsing-text
|
||||
[global importer list]: modules.md#global-importer-list
|
||||
|
||||
### Executing a File
|
||||
|
||||
This algorithm takes a [source file][] `file`, a [configuration][] `config`, an
|
||||
|
@ -12,6 +12,7 @@
|
||||
* [`MinMaxExpression`](#minmaxexpression)
|
||||
* [`PseudoSelector`](#pseudoselector)
|
||||
* [Procedures](#procedures)
|
||||
* [Parsing Text](#parsing-text)
|
||||
* [Parsing Text as CSS](#parsing-text-as-css)
|
||||
* [Consuming an Identifier](#consuming-an-identifier)
|
||||
* [Consuming an Interpolated Identifier](#consuming-an-interpolated-identifier)
|
||||
@ -24,9 +25,11 @@
|
||||
|
||||
### Source File
|
||||
|
||||
A *source file* is a Sass abstract syntax tree along with an absolute URL, known
|
||||
as that file's *canonical URL*. A given canonical URL cannot be associated with
|
||||
more than one source file.
|
||||
A *source file* is a Sass abstract syntax tree along with; an absolute URL, known as
|
||||
that file's *canonical URL*; and an [importer]. A given canonical URL cannot be
|
||||
associated with more than one source file.
|
||||
|
||||
[importer]: modules.md#importer
|
||||
|
||||
### Vendor Prefix
|
||||
|
||||
@ -128,6 +131,19 @@ parentheses.
|
||||
|
||||
## Procedures
|
||||
|
||||
### Parsing Text
|
||||
|
||||
This algorithm takes a string `text` and a syntax `syntax` ("indented", "scss",
|
||||
or "sass"), and returns a Sass abstract syntax tree.
|
||||
|
||||
* If `syntax` is "indented", return the result of parsing `text` as the indented
|
||||
syntax.
|
||||
|
||||
* If `syntax` is "css", return the result of [parsing `text` as
|
||||
CSS](#parsing-text-as-css).
|
||||
|
||||
* If `syntax` is "scss", return the result of parsing `text` as SCSS.
|
||||
|
||||
### Parsing Text as CSS
|
||||
|
||||
This algorithm takes a string, `text`, and returns a Sass abstract syntax tree.
|
||||
|
Loading…
Reference in New Issue
Block a user