mirror of
https://github.com/sass/sass.git
synced 2024-09-21 10:37:22 +00:00
Merge branch 'main' of github.com:sass/sass into feature.color-4
This commit is contained in:
commit
9db0409090
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: bufbuild/buf-setup-action@v1.28.0
|
||||
- uses: bufbuild/buf-setup-action@v1.30.0
|
||||
with: {github_token: "${{ github.token }}"}
|
||||
- name: Generate protobuf code
|
||||
run: buf generate
|
||||
@ -138,7 +138,7 @@ jobs:
|
||||
|
||||
- name: Find changed files in js-api-doc
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@25ef3926d147cd02fc7e931c1ef50772bbb0d25d
|
||||
uses: tj-actions/changed-files@20576b4b9ed46d41e2d45a2256e5e2316dde6834
|
||||
with: {files: js-api-doc}
|
||||
|
||||
- name: Deploy
|
||||
|
@ -4,6 +4,24 @@
|
||||
|
||||
* Remove `RgbColor`, `HslColor` and `HwbColor` SassScript values.
|
||||
|
||||
## 2.5.0
|
||||
|
||||
* Add `NodePackageImporter` as a built-in Package Importer, resolving `pkg:`
|
||||
URLs using the standards and conventions of the Node ecosystem.
|
||||
|
||||
## 2.4.0
|
||||
|
||||
* Add `CompileRequest.silent` option that suppresses all `LogEvent`s.
|
||||
|
||||
## 2.3.0
|
||||
|
||||
* Add a `Value.CompilerMixin` value type to represent first-class mixins.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* Deprecate the `Value.Calculation.CalculationValue.value.interpolation` option,
|
||||
and change how it's interpreted by the compiler.
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* Use the Java package `com.sass_lang.embedded_protocol` and generate multiple
|
||||
|
@ -172,7 +172,7 @@ A number has *known units* unless it has unit `%`.
|
||||
Replace [the definition of `Potentially Slash-Separated Number`] with the
|
||||
following:
|
||||
|
||||
[the definition of `Potentially Slash-Separated Number`]: ../spec/types/number.md#potentially-slash-separated-number
|
||||
[the definition of `Potentially Slash-Separated Number`]: slash-separator.md#existing-behavior
|
||||
|
||||
A Sass number may be *potentially slash-separated*. If it is, it is associated
|
||||
with two additional Sass numbers, the *original numerator* and the *original
|
||||
|
@ -325,7 +325,7 @@ numbers.
|
||||
Add `CalcExpression`s, `ClampExpression`s, `CssMinMax`es to the list of operands
|
||||
of the `/` operator that can create a [potentially slash-separated number].
|
||||
|
||||
[potentially slash-separated number]: ../spec/types/number.md#potentially-slash-separated-number
|
||||
[potentially slash-separated number]: slash-separator.md#existing-behavior
|
||||
|
||||
## Syntax
|
||||
|
||||
|
43
accepted/package-importer.changes.md
Normal file
43
accepted/package-importer.changes.md
Normal file
@ -0,0 +1,43 @@
|
||||
## Draft 1.6
|
||||
|
||||
* Change from an entry point path to entry point directory.
|
||||
|
||||
* Add a private readonly symbol key to `NodePackageImporter` type to
|
||||
distinguish it from arbitrary objects.
|
||||
|
||||
## Draft 1.5
|
||||
|
||||
* Specify that Package Importers must not reject URL patterns that other Package
|
||||
Importers may be able to canonicalize.
|
||||
|
||||
* Specify that the Node Package Importer throws if `entryPointPath` is not
|
||||
passed and `require.main.filename` is not available.
|
||||
|
||||
## Draft 1.4
|
||||
|
||||
* Allow NodePackageImporter to accept an entry point path.
|
||||
|
||||
* Change Embedded protocol entry point to a path.
|
||||
|
||||
## Draft 1.3
|
||||
|
||||
* Handle empty subpath in "Resolving package exports" subprocedure.
|
||||
|
||||
## Draft 1.2
|
||||
|
||||
* Export `NodePackageImporter` type, and set `_NodePackageImporterBrand` to
|
||||
unknown.
|
||||
|
||||
## Draft 1.1
|
||||
|
||||
* Throw an error if `nodePackageImporter` is used in the browser or other
|
||||
environment without filesystem access.
|
||||
|
||||
* Remove specified order in the global import list, as users can specify the
|
||||
order within the `importers` option.
|
||||
|
||||
* Specify importer ordering for the Legacy API.
|
||||
|
||||
## Draft 1
|
||||
|
||||
* Initial draft
|
@ -1,11 +1,10 @@
|
||||
# Package Importer: Draft 1.3
|
||||
# Package Importer: Draft 1.6
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2739))*
|
||||
|
||||
This proposal introduces the semantics for a Package Importer and defines the
|
||||
`pkg:` URL scheme to indicate Sass package imports in an implementation-agnostic
|
||||
format. It also defines the semantics for a new built-in Node Package
|
||||
Importer.
|
||||
format. It also defines the semantics for a new built-in Node Package Importer.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@ -19,7 +18,7 @@ Importer.
|
||||
* [Available in legacy API](#available-in-legacy-api)
|
||||
* [Node Resolution Decisions](#node-resolution-decisions)
|
||||
* [Types](#types)
|
||||
* [`nodePackageImporter`](#nodepackageimporter)
|
||||
* [`NodePackageImporter`](#nodepackageimporter)
|
||||
* [Updated `importers` option](#updated-importers-option)
|
||||
* [Legacy API `pkgImporter`](#legacy-api-pkgimporter)
|
||||
* [Semantics](#semantics)
|
||||
@ -123,9 +122,9 @@ the reader where to find the file.
|
||||
|
||||
While the Dart Sass implementation allows for the use of the `package:` URL
|
||||
scheme, a similar standard doesn't exist in Node. We chose the `pkg:` URL scheme
|
||||
as it clearly communicates to both the user and compiler that the specified files
|
||||
are from a dependency. The `pkg:` URL scheme also does not have known conflicts
|
||||
in the ecosystem.
|
||||
as it clearly communicates to both the user and compiler that the specified
|
||||
files are from a dependency. The `pkg:` URL scheme also does not have known
|
||||
conflicts in the ecosystem.
|
||||
|
||||
#### No built-in `pkg:` resolver for browsers
|
||||
|
||||
@ -188,13 +187,14 @@ use the CSS exports exposed through `"style"`. While in practice, `"style"`
|
||||
tends to be used solely for `css` files, we will support `scss`, `sass` and
|
||||
`css` files for either `"sass"` or `"style"`.
|
||||
|
||||
While conditional exports allows package authors to define specific aliases to internal
|
||||
files, we will still use the Sass conventions for resolving file paths with
|
||||
partials, extensions and indices to discover the intended export alias. However,
|
||||
we will not apply that logic to the destination, and will expect library authors
|
||||
to map the export to the correct place. In other words, given a `package.json`
|
||||
with `exports` as below, The Node package importer will resolve a
|
||||
`@use "pkg:pkgName/variables";` to the destination of the `_variables.scss` export.
|
||||
While conditional exports allows package authors to define specific aliases to
|
||||
internal files, we will still use the Sass conventions for resolving file paths
|
||||
with partials, extensions and indices to discover the intended export alias.
|
||||
However, we will not apply that logic to the destination, and will expect
|
||||
library authors to map the export to the correct place. In other words, given a
|
||||
`package.json` with `exports` as below, The Node package importer will resolve a
|
||||
`@use "pkg:pkgName/variables";` to the destination of the `_variables.scss`
|
||||
export.
|
||||
|
||||
```json
|
||||
{
|
||||
@ -216,19 +216,33 @@ using symlinks if this behavior is desired.
|
||||
|
||||
[ECMAScript modules]: https://nodejs.org/api/esm.html#no-node_path
|
||||
|
||||
The [Node resolution algorithm] requires a `parentURL`, used for determining
|
||||
where in the file system to start searching for a module if a `pkg:` URL is
|
||||
being resolved in a source somewhere other than a file on disk. For instance,
|
||||
when compiling a string like `compileString('@use "pkg:bootstrap";')`, we don't
|
||||
know where to start looking for the Bootstrap module. We considered
|
||||
`require.main.filename` and the current working directory, but found that
|
||||
neither would allow for all use cases. We decided to allow users to specify an
|
||||
entry point directory, defaulting to the parent directory of
|
||||
`require.main.filename`.
|
||||
|
||||
## Types
|
||||
|
||||
```ts
|
||||
import {FileImporter, Importer} from '../spec/js-api/importer';
|
||||
```
|
||||
|
||||
### `nodePackageImporter`
|
||||
### `NodePackageImporter`
|
||||
|
||||
```ts
|
||||
export type NodePackageImporter = {
|
||||
_NodePackageImporterBrand: unknown;
|
||||
};
|
||||
export declare const nodePackageImporter: NodePackageImporter;
|
||||
declare const nodePackageImporterKey: unique symbol;
|
||||
|
||||
export class NodePackageImporter {
|
||||
/** Used to distinguish this type from any arbitrary object. */
|
||||
private readonly [nodePackageImporterKey]: true;
|
||||
|
||||
constructor(entryPointDirectory?: string);
|
||||
}
|
||||
```
|
||||
|
||||
### Updated `importers` option
|
||||
@ -253,19 +267,23 @@ declare module '../spec/js-api/options' {
|
||||
Before the first bullet points in [`compile`] and [`compileString`] in the
|
||||
Javascript Compile API, insert:
|
||||
|
||||
* If any object in `options.importers` is exactly equal to the object
|
||||
`nodePackageImporter`:
|
||||
* If any item in `options.importers` is an instance of the `NodePackageImporter`
|
||||
class:
|
||||
|
||||
* If no filesystem is available, throw an error.
|
||||
|
||||
> This primarily refers to a browser environment, but applies to other
|
||||
> sandboxed JavaScript environments as well.
|
||||
|
||||
* Let `pkgImporter` be a [Node Package Importer] with an associated
|
||||
`entryPointURL` of `require.main.filename`.
|
||||
* Let `entryPointDirectory` be the class's `entryPointDirectory` value if set,
|
||||
resolved relative to the current working directory, and otherwise the parent
|
||||
directory of `require.main.filename`. If `entryPointDirectory` is not passed
|
||||
and `require.main.filename` is not available, throw an error.
|
||||
|
||||
* Replace `nodePackageImporter` with `pkgImporter` in a copy of
|
||||
`options.importers`.
|
||||
* Let `pkgImporter` be a [Node Package Importer] with an associated
|
||||
`entryPointURL` of the absolute file URL for `entryPointDirectory`.
|
||||
|
||||
* Replace the item with `pkgImporter` in a copy of `options.importers`.
|
||||
|
||||
[`compile`]: ../spec/js-api/compile.d.ts.md#compile
|
||||
[`compileString`]: ../spec/js-api/compile.d.ts.md#compilestring
|
||||
@ -278,15 +296,22 @@ any URL with the `pkg:` scheme. This step will be inserted immediately before
|
||||
the existing legacy importer logic, and if the package importer returns `null`,
|
||||
the legacy importer logic will be invoked.
|
||||
|
||||
Currently, the only available package importer is `node`, which follows Node
|
||||
resolution logic to locate Sass files.
|
||||
Currently, the only available package importer is `NodePackageImporter`, which
|
||||
follows Node resolution logic to locate Sass files.
|
||||
|
||||
An optional `entryPointDirectory` path can be passed to the
|
||||
`NodePackageImporter` to provide a starting `parentURL` for the Node package
|
||||
resolution algorithm. If not set, the default value is the parent directory of
|
||||
`require.main.filename`.
|
||||
|
||||
Defaults to undefined.
|
||||
|
||||
```ts
|
||||
import {NodePackageImporter as BaseNodePackageImporter} from '../spec/js-api/importer';
|
||||
|
||||
declare module '../spec/js-api/legacy/options' {
|
||||
export interface LegacySharedOptions<sync extends 'sync' | 'async'> {
|
||||
pkgImporter?: 'node';
|
||||
pkgImporter?: BaseNodePackageImporter;
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -297,7 +322,8 @@ declare module '../spec/js-api/legacy/options' {
|
||||
|
||||
This proposal defines the requirements for Package Importers written by users or
|
||||
provided by implementations. It is a type of [Importer] and, in addition to the
|
||||
standard requirements for importers, it must handle only non-canonical URLs that:
|
||||
standard requirements for importers, it must handle only non-canonical URLs
|
||||
that:
|
||||
|
||||
* have the scheme `pkg`, and
|
||||
* whose path begins with a package name, and
|
||||
@ -315,6 +341,11 @@ Package Importers must reject the following patterns:
|
||||
* A URL whose path begins with `/`.
|
||||
* A URL with non-empty/null username, password, host, port, query, or fragment.
|
||||
|
||||
If the conventions or specifications for an environment disallow any other URL
|
||||
patterns, the Package Importer must return null rather than throwing an error.
|
||||
This allows subsequent Package Importers to attempt to resolve with their
|
||||
conventions.
|
||||
|
||||
[importer]: ../spec/modules.md#importer
|
||||
|
||||
### Node Package Importer
|
||||
@ -344,8 +375,8 @@ When the Node Package Importer is invoked with a string named `string`:
|
||||
|
||||
* Otherwise, let `baseURL` be `entryPointURL`.
|
||||
|
||||
* Let `resolved` be the result of [resolving a `pkg:` URL as Node] with `url` and
|
||||
`baseURL`.
|
||||
* Let `resolved` be the result of [resolving a `pkg:` URL as Node] with `url`
|
||||
and `baseURL`.
|
||||
|
||||
* If `resolved` is null, return null.
|
||||
|
||||
@ -415,8 +446,8 @@ It returns a canonical `file:` URL or null.
|
||||
This algorithm takes a string, `path`, and returns the portion that identifies
|
||||
the Node package.
|
||||
|
||||
* If `path` starts with `@`, it is a scoped package. Return the first 2 [URL path
|
||||
segments], including the separating `/`.
|
||||
* If `path` starts with `@`, it is a scoped package. Return the first 2 [URL
|
||||
path segments], including the separating `/`.
|
||||
|
||||
* Otherwise, return the first URL path segment.
|
||||
|
||||
@ -426,9 +457,13 @@ This algorithm takes a string, `packageName`, and an absolute URL `baseURL`, and
|
||||
returns an absolute URL to the root directory for the most proximate installed
|
||||
`packageName`.
|
||||
|
||||
* Return the result of `PACKAGE_RESOLVE(packageName, baseURL)` as defined in
|
||||
the [Node resolution algorithm specification].
|
||||
* Let `baseDirectory` be `baseURL` appended with a [single-dot URL path
|
||||
segment].
|
||||
|
||||
* Return the result of `PACKAGE_RESOLVE(packageName, baseDirectory)` as defined
|
||||
in the [Node resolution algorithm specification].
|
||||
|
||||
[single-dot URL path segment]: https://url.spec.whatwg.org/#single-dot-path-segment
|
||||
[Node resolution algorithm specification]: https://nodejs.org/api/esm.html#resolution-algorithm-specification
|
||||
|
||||
### Resolving package exports
|
||||
@ -460,7 +495,8 @@ This algorithm takes a package.json value `packageManifest`, a directory URL
|
||||
|
||||
* Let `subpathIndex` be `subpath` + `"/index"`.
|
||||
|
||||
* Let `subpathIndexVariants` be the result of [Export load paths] with `subpathIndex`.
|
||||
* Let `subpathIndexVariants` be the result of [Export load paths] with
|
||||
`subpathIndex`.
|
||||
|
||||
* Let `resolvedIndexPaths` be a list of the results of calling
|
||||
`PACKAGE_EXPORTS_RESOLVE(packageRoot, subpathVariant, exports, ["sass",
|
||||
@ -518,8 +554,8 @@ potential subpaths, resolving for partials and file extensions.
|
||||
|
||||
* Add `subpath` to `paths`.
|
||||
|
||||
* Otherwise, add `subpath` + `.scss`, `subpath` + `.sass`, and `subpath` +
|
||||
`.css` to `paths`.
|
||||
* Otherwise, add `subpath`, `subpath` + `.scss`, `subpath` + `.sass`, and
|
||||
`subpath` + `.css` to `paths`.
|
||||
|
||||
* If `subpath`'s [basename] does not start with `_`, for each `item` in
|
||||
`paths`, prepend `"_"` to the basename, and add to `paths`.
|
||||
@ -531,11 +567,12 @@ potential subpaths, resolving for partials and file extensions.
|
||||
## Embedded Protocol
|
||||
|
||||
An Importer that resolves `pkg:` URLs using the [Node resolution algorithm]. It
|
||||
is instantiated with an associated `entry_point_url`.
|
||||
is instantiated with an associated `entry_point_directory`, which is either
|
||||
absolute or will be resolved relative to the current working directory.
|
||||
|
||||
```proto
|
||||
message NodePackageImporter {
|
||||
string entry_point_url = 1;
|
||||
string entry_point_directory = 1;
|
||||
}
|
||||
```
|
||||
|
@ -1,3 +1,9 @@
|
||||
## Draft 1.2
|
||||
|
||||
* Trigger plain CSS nesting behavior based on the type of *the parent* rule's
|
||||
stylesheet, so that we don't unexpectedly generate plain-CSS nesting for
|
||||
previously un-nested `@import` and `meta.load-css()` rules.
|
||||
|
||||
## Draft 1.1
|
||||
|
||||
* Trigger plain CSS nesting behavior based on the type of a rule's stylesheet,
|
@ -1,4 +1,4 @@
|
||||
# Plain CSS Nesting: Draft 1
|
||||
# Plain CSS Nesting: Draft 1.2
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/3524),
|
||||
[Changelog](plain-css-nesting.changes.md))*
|
||||
@ -93,7 +93,12 @@ To execute a style rule `rule`:
|
||||
* Let `selector` be the result of evaluating all interpolation in `rule`'s
|
||||
selector and parsing the result as a selector list.
|
||||
|
||||
* **If `rule`'s stylesheet wasn't [parsed as CSS]**:
|
||||
* **Let `parent` by the [current style rule] or current at-rule if one exists,
|
||||
or the innermost if both exist.**
|
||||
|
||||
[current style rule]: ../spec/style-rules.md#current-style-rule
|
||||
|
||||
* **If `parent` is a style rule whose stylesheet wasn't [parsed as CSS]:**
|
||||
|
||||
[parsed as CSS]: ../spec/syntax.md#parsing-text-as-css
|
||||
|
||||
@ -101,20 +106,18 @@ To execute a style rule `rule`:
|
||||
> behavior occurs even when plain CSS is evaluated in a Sass context, such as
|
||||
> through a nested `@import` or a `meta.load-css()` call.
|
||||
|
||||
* If there is a [current style rule]:
|
||||
* **If `selector` contains one or more parent selectors and `rule`'s
|
||||
stylesheet wasn't [parsed as CSS], replace those parent selectors with the
|
||||
current style rule's selector and set `selector` to the result.**
|
||||
|
||||
* If `selector` contains one or more parent selectors, replace them with the
|
||||
current style rule's selector and set `selector` to the result.
|
||||
* **Otherwise, nest `selector` within the current style rule's selector using
|
||||
the [descendant combinator] and set `selector` to the result.**
|
||||
|
||||
* Otherwise, nest `selector` within the current style rule's selector using
|
||||
the [descendant combinator] and set `selector` to the result.
|
||||
|
||||
* Otherwise, if `selector` contains one or more parent selectors, throw an
|
||||
error.
|
||||
|
||||
[current style rule]: ../spec/style-rules.md#current-style-rule
|
||||
[descendant combinator]: https://www.w3.org/TR/selectors-3/#descendant-combinators
|
||||
|
||||
* **Otherwise, if `selector` contains one or more parent selectors and `rule`'s
|
||||
stylesheet wasn't [parsed as CSS], throw an error.**
|
||||
|
||||
* Let `css` be a CSS style rule with selector `selector`.
|
||||
|
||||
* Execute each child `child` of `rule`.
|
||||
@ -129,12 +132,11 @@ To execute a style rule `rule`:
|
||||
[complex selectors]: https://drafts.csswg.org/selectors-4/#complex
|
||||
|
||||
* Unless `css`'s selector is now empty:
|
||||
|
||||
* **If `parent` is set and its stylesheet was [parsed as CSS], append `css` to
|
||||
`parent`**
|
||||
|
||||
* **If `rule`'s stylesheet was [parsed as CSS] and there is a [current style
|
||||
rule] or a current at-rule, append `css` to whichever of the two exists, or
|
||||
the innermost if both exist.**
|
||||
|
||||
* **If there is a current at-rule, append `css` to its children.**
|
||||
* **Otherwise, if there is a current at-rule, append `css` to its children.**
|
||||
|
||||
> This was intended to be in the current spec, but was overlooked.
|
||||
|
19
accepted/shared-resources.changes.md
Normal file
19
accepted/shared-resources.changes.md
Normal file
@ -0,0 +1,19 @@
|
||||
## Draft 1.3
|
||||
|
||||
* Expose `Compiler` and `AsyncCompiler` classes.
|
||||
|
||||
* Specify that `Compiler` and `AsyncCompiler` classes throw errors if
|
||||
* constructed without going through the `init` methods.
|
||||
|
||||
## Draft 1.2
|
||||
|
||||
* Await method calls in example code.
|
||||
* Use `compiler.compile*()` methods in example code.
|
||||
|
||||
## Draft 1.1
|
||||
|
||||
* Remove unneeded returned/resolved value from `dispose`.
|
||||
|
||||
## Draft 1
|
||||
|
||||
* Initial draft
|
240
accepted/shared-resources.d.ts.md
Normal file
240
accepted/shared-resources.d.ts.md
Normal file
@ -0,0 +1,240 @@
|
||||
# Shared Resources in JavaScript API: Draft 1.3
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/3296))*
|
||||
|
||||
This proposal adds an API design that allows for sharing resources across
|
||||
multiple invocations of the Sass compiler's JavaScript API. This will provide
|
||||
Sass's users with a more efficient way of running Sass compilations across
|
||||
multiple files.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Summary](#summary)
|
||||
* [Design Decisions](#design-decisions)
|
||||
* [Splitting Sync and Async Compilers](#splitting-sync-and-async-compilers)
|
||||
* [Limited API interface](#limited-api-interface)
|
||||
* [Flexibility for interfaces on process management](#flexibility-for-interfaces-on-process-management)
|
||||
* [No shared state](#no-shared-state)
|
||||
* [Example](#example)
|
||||
* [Sync Compiler](#sync-compiler)
|
||||
* [Async Compiler](#async-compiler)
|
||||
* [API](#api)
|
||||
* [Types](#types)
|
||||
* [initCompiler()](#initcompiler)
|
||||
* [initAsyncCompiler()](#initasynccompiler)
|
||||
* [Compiler](#compiler)
|
||||
* [`compile()` and `compileString()`](#compile-and-compilestring)
|
||||
* [dispose()](#dispose)
|
||||
* [Async Compiler](#async-compiler-1)
|
||||
* [`compileAsync()` and `compileStringAsync()`](#compileasync-and-compilestringasync)
|
||||
* [dispose()](#dispose-1)
|
||||
|
||||
## Summary
|
||||
|
||||
Currently, the JavaScript API for Sass only accommodates a single compilation
|
||||
per process. In practice, we have observed build tools are compiling multiple
|
||||
times in response to a single user action. For instance, Vue.js authors using
|
||||
Vite will see a Sass compilation for each `<style lang="scss">` tag that appears
|
||||
in their codebase.
|
||||
|
||||
While processes can be spun up and down quickly, the combined time can add up to
|
||||
a noticeable impact on performance. The embedded client supports long running
|
||||
processes, and this proposal adds a method for the embedded host to manage the
|
||||
lifecycle of these processes through a Compiler interface.
|
||||
|
||||
### Design Decisions
|
||||
|
||||
#### Splitting Sync and Async Compilers
|
||||
|
||||
When creating a Compiler, users will need to choose either a compiler that
|
||||
provides access to synchronous or asynchronous compilation. While providing both
|
||||
simultaneously from a single Compiler would offer more flexibility, it also adds
|
||||
significant complexity to the API. In practice, we expect most users will only
|
||||
want to use one mode, generally in the mode that is the fastest for the
|
||||
implementation. If synchronous and asynchronous compilations are both needed,
|
||||
users can create multiple Compilers.
|
||||
|
||||
#### Limited API interface
|
||||
|
||||
Many build tools allow passing the Sass module as a parameter, which offers
|
||||
flexibility to users on what implementation of Sass is used. Because users may
|
||||
still want to use portions of the JavaScript API unrelated to compilation, we
|
||||
considered having the Compiler interface mirror the top level Sass interface,
|
||||
which would allow users to replace instances of the imported `sass` class with
|
||||
an instance of the compiler. However, this adds an additional cost to ongoing
|
||||
Sass development. The proposed API does not eliminate this as a possibility in
|
||||
the future.
|
||||
|
||||
#### Flexibility for interfaces on process management
|
||||
|
||||
In environments without access to a long-running compiler—for instance, the Dart
|
||||
Sass implementation—the Compiler interface will continue to perform a single
|
||||
compilation per process.
|
||||
|
||||
#### No shared state
|
||||
|
||||
This proposal does not change how a single compilation is done, and no state is
|
||||
shared across compilations. Options and importers must be set for each
|
||||
compilation. Future enhancements may introduce [shared state], but this proposal
|
||||
only adds the ability to run multiple compilations on a single process.
|
||||
|
||||
[shared state]: https://github.com/sass/sass/issues/3296
|
||||
|
||||
This also means that the proposal makes no assertions about whether file content
|
||||
has changed. It is also up to the user to determine when to start and stop a
|
||||
long-running compiler process.
|
||||
|
||||
### Example
|
||||
|
||||
#### Sync Compiler
|
||||
|
||||
```js
|
||||
import * as sass from 'sass';
|
||||
function setup() {
|
||||
const compiler = sass.initCompiler();
|
||||
const result1 = compiler.compileString('a {b: c}').css;
|
||||
const result2 = compiler.compileString('a {b: c}').css;
|
||||
compiler.dispose();
|
||||
|
||||
// throws error
|
||||
const result3 = compiler.compileString('a {b: c}').css;
|
||||
}
|
||||
```
|
||||
|
||||
#### Async Compiler
|
||||
|
||||
```js
|
||||
import * as sass from 'sass';
|
||||
async function setup() {
|
||||
const compiler = await sass.initAsyncCompiler();
|
||||
const result1 = await compiler.compileStringAsync('a {b: c}').css;
|
||||
const result2 = await compiler.compileStringAsync('a {b: c}').css;
|
||||
await compiler.dispose();
|
||||
|
||||
// throws error
|
||||
const result3 = await compiler.compileStringAsync('a {b: c}').css;
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Types
|
||||
|
||||
```ts
|
||||
import {CompileResult} from '../spec/js-api/compile';
|
||||
import {Options, StringOptions} from '../spec/js-api/options';
|
||||
```
|
||||
|
||||
#### initCompiler()
|
||||
|
||||
Returns a synchronous [Compiler].
|
||||
|
||||
[Compiler]: #compiler
|
||||
|
||||
```ts
|
||||
export function initCompiler(): Compiler;
|
||||
```
|
||||
|
||||
#### initAsyncCompiler()
|
||||
|
||||
Resolves with an [Async Compiler].
|
||||
|
||||
[Async Compiler]: #async-compiler
|
||||
|
||||
```ts
|
||||
export function initAsyncCompiler(): Promise<AsyncCompiler>;
|
||||
```
|
||||
|
||||
#### Compiler
|
||||
|
||||
An instance of the synchronous Compiler.
|
||||
|
||||
```ts
|
||||
export class Compiler {
|
||||
```
|
||||
|
||||
If the Compiler is directly constructed (as opposed to the Compiler being
|
||||
created via `initCompiler`), throw an error.
|
||||
|
||||
```ts
|
||||
private constructor();
|
||||
```
|
||||
|
||||
##### `compile()` and `compileString()`
|
||||
|
||||
Only synchronous compilation methods [`compile()`] and [`compileString()`] must be
|
||||
included, and must have with identical semantics to the [Sass interface].
|
||||
|
||||
[`compile()`]: ../spec/js-api/compile.d.ts.md#compile
|
||||
[`compilestring()`]: ../spec/js-api/compile.d.ts.md#compilestring
|
||||
[Sass interface]: ../spec/js-api/index.d.ts.md
|
||||
|
||||
```ts
|
||||
compile(path: string, options?: Options<'sync'>): CompileResult;
|
||||
compileString(source: string, options?: StringOptions<'sync'>): CompileResult;
|
||||
```
|
||||
|
||||
##### dispose()
|
||||
|
||||
When `dispose` is invoked on a Compiler:
|
||||
|
||||
* Any subsequent invocations of `compile` and `compileString` must throw an
|
||||
error.
|
||||
|
||||
```ts
|
||||
dispose(): void;
|
||||
}
|
||||
```
|
||||
|
||||
#### Async Compiler
|
||||
|
||||
An instance of the asynchronous Compiler.
|
||||
|
||||
```ts
|
||||
export class AsyncCompiler {
|
||||
```
|
||||
|
||||
If the AsyncCompiler is directly constructed (as opposed to the Compiler being
|
||||
created via `initAsyncCompiler`), throw an error.
|
||||
|
||||
```ts
|
||||
private constructor();
|
||||
```
|
||||
|
||||
##### `compileAsync()` and `compileStringAsync()`
|
||||
|
||||
Only asynchronous compilation methods [`compileAsync()`] and
|
||||
[`compileStringAsync()`] must be included, and must have with identical
|
||||
semantics to the [Sass interface].
|
||||
|
||||
[`compileasync()`]: ../spec/js-api/compile.d.ts.md#compileasync
|
||||
[`compilestringasync()`]: ../spec/js-api/compile.d.ts.md#compilestringasync
|
||||
|
||||
```ts
|
||||
compileAsync(
|
||||
path: string,
|
||||
options?: Options<'async'>
|
||||
): Promise<CompileResult>;
|
||||
compileStringAsync(
|
||||
source: string,
|
||||
options?: StringOptions<'async'>
|
||||
): Promise<CompileResult>;
|
||||
```
|
||||
|
||||
##### dispose()
|
||||
|
||||
When `dispose` is invoked on an Async Compiler:
|
||||
|
||||
* Any subsequent invocations of `compileAsync` and `compileStringAsync` must
|
||||
throw an error.
|
||||
|
||||
* Any compilations that have not yet been settled must be allowed to settle, and
|
||||
not be cancelled.
|
||||
|
||||
* Resolves a Promise when all compilations have been settled, and disposal is
|
||||
complete.
|
||||
|
||||
```ts
|
||||
dispose(): Promise<void>;
|
||||
}
|
||||
```
|
@ -1,13 +1,21 @@
|
||||
The [`sass` package] on npm is a pure-JavaScript package built from the [Dart
|
||||
Sass] implementation. In addition to Dart Sass's [command-line interface], it
|
||||
provides a JavaScript API that can be used to drive Sass compilations from
|
||||
The Sass JavaScript API can be used to to drive Sass Compilations from
|
||||
JavaScript. It even allows an application to control {@link Options.importers |
|
||||
how stylesheets are loaded} and {@link Options.functions | define custom
|
||||
functions}.
|
||||
|
||||
The [`sass` package] on npm is a pure-JavaScript package built from the [Dart
|
||||
Sass] implementation, and includes Dart Sass's [command-line interface].
|
||||
|
||||
The [`sass-embedded` package] on npm is a JavaScript wrapper around a native
|
||||
Dart executable, and in general is faster than `sass`.
|
||||
|
||||
Both `sass` and `sass-embedded` provide the same JavaScript API using the same
|
||||
underlying [Dart Sass] implementation, but have speed and platform tradeoffs.
|
||||
|
||||
[`sass` package]: https://www.npmjs.com/package/sass
|
||||
[Dart Sass]: https://sass-lang.com/dart-sass
|
||||
[command-line interface]: https://sass-lang.com/documentation/cli/dart-sass
|
||||
[`sass-embedded` package]: https://www.npmjs.com/package/sass-embedded
|
||||
|
||||
## Usage
|
||||
|
||||
@ -112,3 +120,38 @@ of Sass code by passing in a {@link LegacyStringOptions}.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Speed
|
||||
|
||||
While multiple factors go into how long Sass compilations take, there are
|
||||
general speed trends that can help you minimize your compilation time.
|
||||
|
||||
### With the `sass` package
|
||||
|
||||
With the `sass` package, the synchronous calls will be faster than asynchronous
|
||||
calls due to the overhead of making the entire evaluation process asynchronous.
|
||||
While the {@link Compiler} and {@link AsyncCompiler} class are available, they
|
||||
aren't faster than than the module-level compilation methods when using `sass`.
|
||||
|
||||
### With the `sass-embedded` package
|
||||
|
||||
The `sass-embedded` package provides significant speed improvements in certain
|
||||
situations, and is generally faster than `sass` for large or frequent
|
||||
compilations. When using the module-level compilation methods, asynchronous
|
||||
calls are generally faster than synchronous ones due to the overhead of
|
||||
emulating synchronous messaging with worker threads and concurrent compilations
|
||||
being blocked on the main thread.
|
||||
|
||||
The {@link Compiler} and {@link AsyncCompiler} classes provide significant
|
||||
improvements when using the `sass-embedded` package. These classes persist and
|
||||
reuse a single Dart process across multiple compilations, avoiding the need to
|
||||
repeatedly start up and tear down a process.
|
||||
|
||||
When compiling a single file using `sass-embedded`, there is not much difference
|
||||
between the synchronous and asynchronous methods. When running multiple
|
||||
compilations at the same time, an {@link AsyncCompiler} will be considerably
|
||||
faster than a synchronous {@link Compiler}.
|
||||
|
||||
Other factors like {@link Functions}, {@link Importers} and the complexity of
|
||||
your Sass files may also impact what compilation methods work best for your
|
||||
particular use case.
|
||||
|
190
js-api-doc/compile.d.ts
vendored
190
js-api-doc/compile.d.ts
vendored
@ -37,6 +37,104 @@ export interface CompileResult {
|
||||
sourceMap?: RawSourceMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of creating a synchronous compiler. Returned by
|
||||
* {@link initCompiler}.
|
||||
*
|
||||
* @category Compile
|
||||
*/
|
||||
export class Compiler {
|
||||
/**
|
||||
* Throws an error if constructed directly, instead of via
|
||||
* {@link initCompiler}.
|
||||
*/
|
||||
private constructor();
|
||||
|
||||
/**
|
||||
* The {@link compile} method exposed through a Compiler instance while it is
|
||||
* active. If this is called after {@link dispose} on the Compiler
|
||||
* instance, an error will be thrown.
|
||||
*
|
||||
* During the Compiler instance's lifespan, given the same input, this will
|
||||
* return an identical result to the {@link compile} method exposed at the
|
||||
* module root.
|
||||
*/
|
||||
compile(path: string, options?: Options<'sync'>): CompileResult;
|
||||
|
||||
/**
|
||||
* The {@link compileString} method exposed through a Compiler instance while
|
||||
* it is active. If this is called after {@link dispose} on the Compiler
|
||||
* instance, an error will be thrown.
|
||||
*
|
||||
* During the Compiler instance's lifespan, given the same input, this will
|
||||
* return an identical result to the {@link compileString} method exposed at
|
||||
* the module root.
|
||||
*/
|
||||
compileString(source: string, options?: StringOptions<'sync'>): CompileResult;
|
||||
|
||||
/**
|
||||
* Ends the lifespan of this Compiler instance. After this is invoked, all
|
||||
* calls to the Compiler instance's {@link compile} or {@link compileString}
|
||||
* methods will result in an error.
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of creating an asynchronous compiler. Returned by
|
||||
* {@link initAsyncCompiler}.
|
||||
*
|
||||
* @category Compile
|
||||
*/
|
||||
export class AsyncCompiler {
|
||||
/**
|
||||
* Throws an error if constructed directly, instead of via
|
||||
* {@link initAsyncCompiler}.
|
||||
*/
|
||||
private constructor();
|
||||
|
||||
/**
|
||||
* The {@link compileAsync} method exposed through an Async Compiler instance
|
||||
* while it is active. If this is called after {@link dispose} on the Async
|
||||
* Compiler instance, an error will be thrown.
|
||||
*
|
||||
* During the Async Compiler instance's lifespan, given the same input, this
|
||||
* will return an identical result to the {@link compileAsync} method exposed
|
||||
* at the module root.
|
||||
*/
|
||||
compileAsync(
|
||||
path: string,
|
||||
options?: Options<'async'>
|
||||
): Promise<CompileResult>;
|
||||
|
||||
/**
|
||||
* The {@link compileStringAsync} method exposed through an Async Compiler
|
||||
* instance while it is active. If this is called after {@link dispose} on the
|
||||
* Async Compiler instance, an error will be thrown.
|
||||
*
|
||||
* During the Async Compiler instance's lifespan, given the same input, this
|
||||
* will return an identical result to the {@link compileStringAsync} method
|
||||
* exposed at the module root.
|
||||
*/
|
||||
compileStringAsync(
|
||||
source: string,
|
||||
options?: StringOptions<'async'>
|
||||
): Promise<CompileResult>;
|
||||
|
||||
/**
|
||||
* Ends the lifespan of this Async Compiler instance. After this is invoked,
|
||||
* all subsequent calls to the Compiler instance's `compileAsync` or
|
||||
* `compileStringAsync` methods will result in an error.
|
||||
*
|
||||
* Any compilations that are submitted before `dispose` will not be cancelled,
|
||||
* and will be allowed to settle.
|
||||
*
|
||||
* After all compilations have been settled and Sass completes any internal
|
||||
* task cleanup, `dispose` will resolve its promise.
|
||||
*/
|
||||
dispose(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously compiles the Sass file at `path` to CSS. If it succeeds it
|
||||
* returns a {@link CompileResult}, and if it fails it throws an {@link
|
||||
@ -44,10 +142,16 @@ export interface CompileResult {
|
||||
*
|
||||
* This only allows synchronous {@link Importer}s and {@link CustomFunction}s.
|
||||
*
|
||||
* **Heads up!** When using the `sass-embedded` npm package,
|
||||
* **{@link compileAsync} is almost always faster than {@link compile}**, due to
|
||||
* the overhead of emulating synchronous messaging with worker threads and
|
||||
* concurrent compilations being blocked on main thread.
|
||||
* **Heads up!** When using the [sass-embedded] npm package for single
|
||||
* compilations, **{@link compileAsync} is almost always faster than
|
||||
* {@link compile}**, due to the overhead of emulating synchronous messaging
|
||||
* with worker threads and concurrent compilations being blocked on main thread.
|
||||
*
|
||||
* If you are running multiple compilations with the [sass-embedded] npm
|
||||
* package, using a {@link Compiler} will provide some speed improvements over
|
||||
* the module-level methods, and an {@link AsyncCompiler} will be much faster.
|
||||
*
|
||||
* [sass-embedded]: https://www.npmjs.com/package/sass-embedded
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@ -99,12 +203,18 @@ export function compileAsync(
|
||||
*
|
||||
* This only allows synchronous {@link Importer}s and {@link CustomFunction}s.
|
||||
*
|
||||
* **Heads up!** When using the `sass-embedded` npm package,
|
||||
* **{@link compileStringAsync} is almost always faster than
|
||||
* **Heads up!** When using the [sass-embedded] npm package for single
|
||||
* compilations, **{@link compileStringAsync} is almost always faster than
|
||||
* {@link compileString}**, due to the overhead of emulating synchronous
|
||||
* messaging with worker threads and concurrent compilations being blocked on
|
||||
* main thread.
|
||||
*
|
||||
* If you are running multiple compilations with the [sass-embedded] npm
|
||||
* package, using a {@link Compiler} will provide some speed improvements over
|
||||
* the module-level methods, and an {@link AsyncCompiler} will be much faster.
|
||||
*
|
||||
* [sass-embedded]: https://www.npmjs.com/package/sass-embedded
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
@ -162,3 +272,71 @@ export function compileStringAsync(
|
||||
source: string,
|
||||
options?: StringOptions<'async'>
|
||||
): Promise<CompileResult>;
|
||||
|
||||
/**
|
||||
* Creates a synchronous {@link Compiler}. Each compiler instance exposes the
|
||||
* {@link compile} and {@link compileString} methods within the lifespan of the
|
||||
* Compiler. Given identical input, these methods will return results identical
|
||||
* to their counterparts exposed at the module root. To use asynchronous
|
||||
* compilation, use {@link initAsyncCompiler}.
|
||||
*
|
||||
* When calling the compile functions multiple times, using a compiler instance
|
||||
* with the [sass-embedded] npm package is much faster than using the top-level
|
||||
* compilation methods or the [sass] npm package.
|
||||
*
|
||||
* [sass-embedded]: https://www.npmjs.com/package/sass-embedded
|
||||
*
|
||||
* [sass]: https://www.npmjs.com/package/sass
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* const sass = require('sass');
|
||||
* function setup() {
|
||||
* const compiler = sass.initCompiler();
|
||||
* const result1 = compiler.compileString('a {b: c}').css;
|
||||
* const result2 = compiler.compileString('a {b: c}').css;
|
||||
* compiler.dispose();
|
||||
*
|
||||
* // throws error
|
||||
* const result3 = sass.compileString('a {b: c}').css;
|
||||
* }
|
||||
* ```
|
||||
* @category Compile
|
||||
* @compatibility dart: "1.70.0", node: false
|
||||
*/
|
||||
export function initCompiler(): Compiler;
|
||||
|
||||
/**
|
||||
* Creates an asynchronous {@link AsyncCompiler}. Each compiler
|
||||
* instance exposes the {@link compileAsync} and {@link compileStringAsync}
|
||||
* methods within the lifespan of the Compiler. Given identical input, these
|
||||
* methods will return results identical to their counterparts exposed at the
|
||||
* module root. To use synchronous compilation, use {@link initCompiler};
|
||||
*
|
||||
* When calling the compile functions multiple times, using a compiler instance
|
||||
* with the [sass-embedded] npm package is much faster than using the top-level
|
||||
* compilation methods or the [sass] npm package.
|
||||
*
|
||||
* [sass-embedded]: https://www.npmjs.com/package/sass-embedded
|
||||
*
|
||||
* [sass]: https://www.npmjs.com/package/sass
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* const sass = require('sass');
|
||||
* async function setup() {
|
||||
* const compiler = await sass.initAsyncCompiler();
|
||||
* const result1 = await compiler.compileStringAsync('a {b: c}').css;
|
||||
* const result2 = await compiler.compileStringAsync('a {b: c}').css;
|
||||
* await compiler.dispose();
|
||||
*
|
||||
* // throws error
|
||||
* const result3 = await sass.compileStringAsync('a {b: c}').css;
|
||||
* }
|
||||
* ```
|
||||
* @category Compile
|
||||
* @compatibility dart: "1.70.0", node: false
|
||||
*/
|
||||
export function initAsyncCompiler(): Promise<AsyncCompiler>;
|
||||
|
133
js-api-doc/importer.d.ts
vendored
133
js-api-doc/importer.d.ts
vendored
@ -308,6 +308,139 @@ export interface Importer<sync extends 'sync' | 'async' = 'sync' | 'async'> {
|
||||
nonCanonicalScheme?: string | string[];
|
||||
}
|
||||
|
||||
declare const nodePackageImporterKey: unique symbol;
|
||||
|
||||
/**
|
||||
* The built-in Node.js package importer. This loads pkg: URLs from node_modules
|
||||
* according to the standard Node.js resolution algorithm.
|
||||
*
|
||||
* A Node.js package importer is exposed as a class that can be added to the
|
||||
* `importers` option.
|
||||
*
|
||||
*```js
|
||||
* const sass = require('sass');
|
||||
* sass.compileString('@use "pkg:vuetify', {
|
||||
* importers: [new sass.NodePackageImporter()]
|
||||
* });
|
||||
*```
|
||||
*
|
||||
* ## Writing Sass packages
|
||||
*
|
||||
* Package authors can control what is exposed to their users through their
|
||||
* `package.json` manifest. The recommended method is to add a `sass`
|
||||
* conditional export to `package.json`.
|
||||
*
|
||||
* ```json
|
||||
* // node_modules/uicomponents/package.json
|
||||
* {
|
||||
* "exports": {
|
||||
* ".": {
|
||||
* "sass": "./src/scss/index.scss",
|
||||
* "import": "./dist/js/index.mjs",
|
||||
* "default": "./dist/js/index.js"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This allows a package user to write `@use "pkg:uicomponents"` to load the
|
||||
* file at `node_modules/uicomponents/src/scss/index.scss`.
|
||||
*
|
||||
* The Node.js package importer supports the variety of formats supported by
|
||||
* Node.js [package entry points], allowing authors to expose multiple subpaths.
|
||||
*
|
||||
* [package entry points]:
|
||||
* https://nodejs.org/api/packages.html#package-entry-points
|
||||
*
|
||||
* ```json
|
||||
* // node_modules/uicomponents/package.json
|
||||
* {
|
||||
* "exports": {
|
||||
* ".": {
|
||||
* "sass": "./src/scss/index.scss",
|
||||
* },
|
||||
* "./colors.scss": {
|
||||
* "sass": "./src/scss/_colors.scss",
|
||||
* },
|
||||
* "./theme/*.scss": {
|
||||
* "sass": "./src/scss/theme/*.scss",
|
||||
* },
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This allows a package user to write:
|
||||
*
|
||||
* - `@use "pkg:uicomponents";` to import the root export.
|
||||
* - `@use "pkg:uicomponents/colors";` to import the colors partial.
|
||||
* - `@use "pkg:uicomponents/theme/purple";` to import a purple theme.
|
||||
*
|
||||
* Note that while library users can rely on the importer to resolve
|
||||
* [partials](https://sass-lang.com/documentation/at-rules/use#partials), [index
|
||||
* files](https://sass-lang.com/documentation/at-rules/use#index-files), and
|
||||
* extensions, library authors must specify the entire file path in `exports`.
|
||||
*
|
||||
* In addition to the `sass` condition, the `style` condition is also
|
||||
* acceptable. Sass will match the `default` condition if it's a relevant file
|
||||
* type, but authors are discouraged from relying on this. Notably, the key
|
||||
* order matters, and the importer will resolve to the first value with a key
|
||||
* that is `sass`, `style`, or `default`, so you should always put `default`
|
||||
* last.
|
||||
*
|
||||
* To help package authors who haven't transitioned to package entry points
|
||||
* using the `exports` field, the Node.js package importer provides several
|
||||
* fallback options. If the `pkg:` URL does not have a subpath, the Node.js
|
||||
* package importer will look for a `sass` or `style` key at the root of
|
||||
* `package.json`.
|
||||
*
|
||||
* ```json
|
||||
* // node_modules/uicomponents/package.json
|
||||
* {
|
||||
* "sass": "./src/scss/index.scss",
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This allows a user to write `@use "pkg:uicomponents";` to import the
|
||||
* `index.scss` file.
|
||||
*
|
||||
* Finally, the Node.js package importer will look for an `index` file at the
|
||||
* package root, resolving partials and extensions. For example, if the file
|
||||
* `_index.scss` exists in the package root of `uicomponents`, a user can import
|
||||
* that with `@use "pkg:uicomponents";`.
|
||||
*
|
||||
* If a `pkg:` URL includes a subpath that doesn't have a match in package entry
|
||||
* points, the Node.js importer will attempt to find that file relative to the
|
||||
* package root, resolving for file extensions, partials and index files. For
|
||||
* example, if the file `src/sass/_colors.scss` exists in the `uicomponents`
|
||||
* package, a user can import that file using `@use
|
||||
* "pkg:uicomponents/src/sass/colors";`.
|
||||
*
|
||||
* @compatibility dart: "1.71.0", node: false
|
||||
* @category Importer
|
||||
*/
|
||||
export class NodePackageImporter {
|
||||
/** Used to distinguish this type from any arbitrary object. */
|
||||
private readonly [nodePackageImporterKey]: true;
|
||||
|
||||
/**
|
||||
* The NodePackageImporter has an optional `entryPointDirectory` option, which
|
||||
* is the directory where the Node Package Importer should start when
|
||||
* resolving `pkg:` URLs in sources other than files on disk. This will be
|
||||
* used as the `parentURL` in the [Node Module
|
||||
* Resolution](https://nodejs.org/api/esm.html#resolution-algorithm-specification)
|
||||
* algorithm.
|
||||
*
|
||||
* In order to be found by the Node Package Importer, a package will need to
|
||||
* be inside a node_modules folder located in the `entryPointDirectory`, or
|
||||
* one of its parent directories, up to the filesystem root.
|
||||
*
|
||||
* Relative paths will be resolved relative to the current working directory.
|
||||
* If a path is not provided, this defaults to the parent directory of the
|
||||
* Node.js entrypoint. If that's not available, this will throw an error.
|
||||
*/
|
||||
constructor(entryPointDirectory?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of successfully loading a stylesheet with an {@link Importer}.
|
||||
*
|
||||
|
5
js-api-doc/index.d.ts
vendored
5
js-api-doc/index.d.ts
vendored
@ -3,11 +3,15 @@
|
||||
// implementations.
|
||||
|
||||
export {
|
||||
AsyncCompiler,
|
||||
CompileResult,
|
||||
Compiler,
|
||||
compile,
|
||||
compileAsync,
|
||||
compileString,
|
||||
compileStringAsync,
|
||||
initCompiler,
|
||||
initAsyncCompiler,
|
||||
} from './compile';
|
||||
export {Exception} from './exception';
|
||||
export {
|
||||
@ -15,6 +19,7 @@ export {
|
||||
FileImporter,
|
||||
Importer,
|
||||
ImporterResult,
|
||||
NodePackageImporter,
|
||||
} from './importer';
|
||||
export {Logger, SourceSpan, SourceLocation} from './logger';
|
||||
export {
|
||||
|
19
js-api-doc/legacy/options.d.ts
vendored
19
js-api-doc/legacy/options.d.ts
vendored
@ -1,6 +1,7 @@
|
||||
import {Logger} from '../logger';
|
||||
import {LegacyImporter} from './importer';
|
||||
import {LegacyFunction} from './function';
|
||||
import {NodePackageImporter} from '../importer';
|
||||
|
||||
/**
|
||||
* Options for {@link render} and {@link renderSync} that are shared between
|
||||
@ -508,6 +509,24 @@ export interface LegacySharedOptions<sync extends 'sync' | 'async'> {
|
||||
* @compatibility dart: "1.43.0", node: false
|
||||
*/
|
||||
logger?: Logger;
|
||||
|
||||
/**
|
||||
* If this option is set to an instance of `NodePackageImporter`, Sass will
|
||||
* use the built-in Node.js package importer to resolve Sass files with a
|
||||
* `pkg:` URL scheme. Details for library authors and users can be found in
|
||||
* the {@link NodePackageImporter} documentation.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* sass.renderSync({
|
||||
* data: '@use "pkg:vuetify";',
|
||||
* pkgImporter: new sass.NodePackageImporter()
|
||||
* });
|
||||
* ```
|
||||
* @category Plugins
|
||||
* @compatibility dart: "2.0", node: false
|
||||
*/
|
||||
pkgImporter?: NodePackageImporter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
8
js-api-doc/options.d.ts
vendored
8
js-api-doc/options.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
import {FileImporter, Importer} from './importer';
|
||||
import {FileImporter, Importer, NodePackageImporter} from './importer';
|
||||
import {Logger} from './logger';
|
||||
import {Value} from './value';
|
||||
import {PromiseOr} from './util/promise_or';
|
||||
@ -208,8 +208,8 @@ export interface Options<sync extends 'sync' | 'async'> {
|
||||
* - The importer that was used to load the current stylesheet, with the
|
||||
* loaded URL resolved relative to the current stylesheet's canonical URL.
|
||||
*
|
||||
* - Each {@link Importer} or {@link FileImporter} in {@link importers}, in
|
||||
* order.
|
||||
* - Each {@link Importer}, {@link FileImporter}, or
|
||||
* {@link NodePackageImporter} in {@link importers}, in order.
|
||||
*
|
||||
* - Each load path in {@link loadPaths}, in order.
|
||||
*
|
||||
@ -218,7 +218,7 @@ export interface Options<sync extends 'sync' | 'async'> {
|
||||
*
|
||||
* @category Plugins
|
||||
*/
|
||||
importers?: (Importer<sync> | FileImporter<sync>)[];
|
||||
importers?: (Importer<sync> | FileImporter<sync> | NodePackageImporter)[];
|
||||
|
||||
/**
|
||||
* Paths in which to look for stylesheets loaded by rules like
|
||||
|
@ -31,6 +31,12 @@ colors outside the sRGB gamut.
|
||||
* [`color.mix()`](#colormix)
|
||||
* [Deprecations](#deprecations)
|
||||
* [Design Decisions](#design-decisions)
|
||||
* [Unclamped Channels](#unclamped-channels)
|
||||
* [Clamped Channels](#clamped-channels)
|
||||
* [Changing Color Spaces](#changing-color-spaces)
|
||||
* [Gamut Mapping](#gamut-mapping)
|
||||
* [CSS Color 5](#css-color-5)
|
||||
* [Special Thanks](#special-thanks)
|
||||
* [Definitions](#definitions)
|
||||
* [Color](#color)
|
||||
* [Legacy Color](#legacy-color)
|
||||
@ -42,11 +48,12 @@ colors outside the sRGB gamut.
|
||||
* [Color Interpolation Method](#color-interpolation-method)
|
||||
* [Serialization](#serialization)
|
||||
* [Serialization of Non-Legacy Colors](#serialization-of-non-legacy-colors)
|
||||
* [Serialization of Out-of-Gamut RGB Colors](#serialization-of-out-of-gamut-rgb-colors)
|
||||
* [Procedures](#procedures)
|
||||
* [Looking Up a Known Color Space](#looking-up-a-known-color-space)
|
||||
* [Converting a Color](#converting-a-color)
|
||||
* [CSS-Converting a Color Space](#css-converting-a-color-space)
|
||||
* [Gamut Mapping](#gamut-mapping)
|
||||
* [CSS-Converting a Color Space](#css-converting-a-color-space)
|
||||
* [Gamut Mapping](#gamut-mapping-1)
|
||||
* [Parsing Color Components](#parsing-color-components)
|
||||
* [Percent-Converting a Number](#percent-converting-a-number)
|
||||
* [Validating a Color Channel](#validating-a-color-channel)
|
||||
@ -420,21 +427,66 @@ being deprecated in favor of color-space-friendly functions like
|
||||
|
||||
### Design Decisions
|
||||
|
||||
Most of the design decisions involved in the proposal are based on the
|
||||
[CSS Color Level 4][color-4] specification, which we have tried to emulate as
|
||||
closely as possible, while maintaining support for legacy projects. In some
|
||||
cases, that required major changes to the way Sass handles colors:
|
||||
#### Unclamped Channels
|
||||
|
||||
Most of the design decisions involved in the proposal are based on the [CSS
|
||||
Color Level 4][color-4] specification, which we have tried to emulate as closely
|
||||
as possible while maintaining support for legacy projects. In some cases, that
|
||||
required major changes to the way Sass handles colors:
|
||||
|
||||
1. Channel values are no longer internally clamped to the gamut of a color
|
||||
space. By default Sass will output CSS with out-of-gamut colors, because
|
||||
these colors are handled differently when doing interpolation across
|
||||
different color spaces. Browsers may also eventually handle gamut-mapping for
|
||||
these colors, although at the time of writing they do not. Authors can also
|
||||
use the provided `color.to-gamut()` function to force a color to be mapped
|
||||
into the gamut of its native color space.
|
||||
|
||||
1. RGB-style channel values are no longer clamped to the gamut of a color space,
|
||||
except for the `hsl` and `hwb` spaces, which are unable to represent
|
||||
out-of-gamut colors. By default Sass will output CSS with out-of-gamut
|
||||
colors, because browsers can provide better gamut mapping based on the user
|
||||
device capabilities. However, authors can use the provided `color.to-gamut()`
|
||||
function to enforce mapping a color into a specific gamut.
|
||||
2. RGB-style channel values are no longer rounded to the nearest integer, since
|
||||
the spec now requires maintaining precision wherever possible. This is
|
||||
especially important in RGB spaces, where color distribution is inconsistent.
|
||||
|
||||
#### Clamped Channels
|
||||
|
||||
Per the CSS specs, certain channels are clamped at parse time [but *not* in
|
||||
interpolation] for specific color functions:
|
||||
|
||||
[but *not* in interpolation]: https://github.com/w3c/csswg-drafts/issues/9484
|
||||
|
||||
* All channels of the `rgb()` and `rgba()` functions.
|
||||
* The lightness channel of the `lab()`, `lch()`, `oklab()`, and `oklch()`
|
||||
functions.
|
||||
* The lower bound of the chroma channel of the `lch()` and `oklch()` functions.
|
||||
|
||||
However, it's necessary to use out-of-gamut values in these spaces to represent
|
||||
valid colors from other spaces—for example, `color(xyz 1 1 1)` is equivalent to
|
||||
`lab(100.12% 9.0645 5.8018)` and `color(prophoto-rgb 0 1 0)` is equivalent to
|
||||
`rgb(-221.6192400378, 279.4082218845, -109.1140773956)`. To match the behavior
|
||||
of CSS, we have to clamp these channels when constructing colors directly
|
||||
through color functions, so writing `lab(110% 0 0)` will return `lab(100% 0 0)`.
|
||||
On the other hand, to preserve colors on a round trip between spaces, we need to
|
||||
allow the internal representation of these colors to go out-of-gamut.
|
||||
|
||||
The question remains as to how to handle cases that don't directly correspond to
|
||||
CSS, such as the `color.change()` function. Because out-of-gamut clamped
|
||||
channels are meaningful in CSS, we've chosen the design principle of preserving
|
||||
them in all situations where clamping isn't specifically mandated by CSS. In
|
||||
addition, to ensure that that the colors represent the same values in CSS that
|
||||
they do in Sass, they'll be serialized to special formats that preserves their
|
||||
out-of-gamut values:
|
||||
|
||||
* Out-of-gamut RGB colors are serialized to `hsl()`, the lightness channel and
|
||||
saturation upper bound of which per spec are not clamped at parse-time
|
||||
(although in practice browsers do clamp them at time of writing). This ensures
|
||||
that older browsers will still handle these colors somewhat correctly while
|
||||
preserving the unclamped value for modern browsers (once they work per spec).
|
||||
|
||||
* Out-of-gamut Lab, LCH, OKLab, and OKLCH colors are serialized to `color-mix(in
|
||||
..., color(xyz ...) 100%, black)` to preserve both the original color space
|
||||
and the unclamped value in conformant browsers.
|
||||
|
||||
#### Changing Color Spaces
|
||||
|
||||
Different color spaces often represent different color-gamuts, which can present
|
||||
a new set of problems for authors. Some color manipulations are best handled
|
||||
in a wide-gamut space like `oklch`, but (for now) authors will likely prefer
|
||||
@ -454,6 +506,8 @@ and mapping in Sass color functions:
|
||||
* No color function performs gamut-mapping on out-of-gamut channels, except
|
||||
`color.to-gamut()`, which can be used for manual gamut-mapping.
|
||||
|
||||
#### Gamut Mapping
|
||||
|
||||
Browsers currently use channel-clipping rather than the proposed
|
||||
[css gamut mapping algorithm][css-mapping] to handle colors that cannot be
|
||||
shown correctly on a given display. We've decided to provide `color.to-gamut()`
|
||||
@ -463,6 +517,8 @@ will consider adding an additional algorithm-selection argument. However, the
|
||||
primary goal of this function is not to match CSS behavior, but to provide a
|
||||
better mapping than the default channel-clipping.
|
||||
|
||||
#### CSS Color 5
|
||||
|
||||
We are not attempting to support all of [CSS Color Level 5][color-5] at this
|
||||
point, since it is not yet implemented in browsers. However, we have used it as
|
||||
a reference while updating color manipulation functions such as `color.mix()`.
|
||||
@ -479,6 +535,8 @@ provides us with the most flexibility to change our behavior in the future.
|
||||
[color-5]: https://www.w3.org/TR/css-color-5/
|
||||
[relative color syntax]: https://drafts.csswg.org/css-color-5/#relative-colors
|
||||
|
||||
#### Special Thanks
|
||||
|
||||
Thanks to the editors of the CSS Color Level 4 specification (Tab Atkins Jr.,
|
||||
Chris Lilley, and Lea Verou) for answering our many questions along the way. We
|
||||
also used Chris and Lea's [Color.js](https://colorjs.io/) library as a
|
||||
@ -554,9 +612,10 @@ unquoted lowercase strings by inspection functions.
|
||||
|
||||
Values outside a *bounded gamut* range (including infinity or negative infinity)
|
||||
are valid but are considered *out of gamut* for the given color space. They
|
||||
remain un-clamped unless the gamut is specifically marked as "clamped". If the
|
||||
channel is bounded, or has a percentage mapping, then the channel is considered
|
||||
*scalable*.
|
||||
remain un-clamped unless the gamut is specifically marked as "clamped", in which
|
||||
case they're clamped *only* when constructing the color from its global
|
||||
constructor function. If the channel is bounded, or has a percentage mapping,
|
||||
then the channel is considered *scalable*.
|
||||
|
||||
Some color spaces use a *polar angle* value for the `hue` channel. Polar-angle
|
||||
hues represent an angle position around a given hue wheel, using a CSS `<angle>`
|
||||
@ -570,7 +629,7 @@ The known color spaces and their channels are:
|
||||
|
||||
* `rgb` (RGB, legacy):
|
||||
* `red`, `green`, `blue`:
|
||||
* gamut: bounded
|
||||
* gamut: bounded, clamped
|
||||
* number: `[0,255]`
|
||||
|
||||
> Percentages `[0%,100%]` map to the `[0,255]` range.
|
||||
@ -589,11 +648,11 @@ The known color spaces and their channels are:
|
||||
* associated unit: `deg`
|
||||
* degrees: polar angle
|
||||
* `saturation`:
|
||||
* gamut: bounded
|
||||
* gamut: bounded, clamped (lower bound only)
|
||||
* associated unit: `%`
|
||||
* percentage: `[0%,100%]`
|
||||
* `lightness`:
|
||||
* gamut: bounded, clamped
|
||||
* gamut: bounded
|
||||
* associated unit: `%`
|
||||
* percentage: `[0%,100%]`
|
||||
|
||||
@ -635,7 +694,7 @@ The known color spaces and their channels are:
|
||||
> Percentages `[0%,100%]` map to the `[0,100]` range.
|
||||
|
||||
* `chroma`:
|
||||
* gamut: un-bounded
|
||||
* gamut: un-bounded, clamped (lower bound only)
|
||||
* number: `[0,150]`
|
||||
|
||||
> Percentages `[0%,100%]` map to the `[0,150]` range.
|
||||
@ -667,7 +726,7 @@ The known color spaces and their channels are:
|
||||
> Percentages `[0%,100%]` map to the `[0,1]` range.
|
||||
|
||||
* `chroma`:
|
||||
* gamut: un-bounded
|
||||
* gamut: un-bounded, clamped (lower bound only)
|
||||
* number: `[0,0.4]`
|
||||
|
||||
> Percentages `[0%,100%]` map to the `[0,0.4]` range.
|
||||
@ -786,6 +845,18 @@ To serialize a non-legacy color `color`:
|
||||
|
||||
* Let `space-name` be an unquoted lowercase string of `color`'s space name.
|
||||
|
||||
* If `color` has a clamped channel whose value is out-of-bounds:
|
||||
|
||||
* Let `xyz` be the result of [converting] `color` into the `xyz` color space.
|
||||
|
||||
* Emit "color-mix(in ", followed by `space-name`, ", ", the result of
|
||||
serializing `xyz`, and then " 100%, black)".
|
||||
|
||||
> This syntax effectively converts the `xyz` color back into the original
|
||||
> color space. The second argument to `color-mix()` is irrelevant when the
|
||||
> first color has weight 100%, so implementations are free to change it (for
|
||||
> example, an implementation might use "red" instead to minimize bytes).
|
||||
|
||||
* Let `known-space` be the result of [looking up a known color space] with a
|
||||
`name` of `space-name`.
|
||||
|
||||
@ -824,6 +895,14 @@ To serialize a non-legacy color `color`:
|
||||
|
||||
[predefined color space]: #predefined-color-spaces
|
||||
|
||||
### Serialization of Out-of-Gamut RGB Colors
|
||||
|
||||
To serialize an out-of-gamut color `color` in the `rgb` space:
|
||||
|
||||
* Let `hsl` be the result of [converting] `color` into the `hsl` space.
|
||||
|
||||
* Return the result of serializing `hsl`.
|
||||
|
||||
## Procedures
|
||||
|
||||
### Looking Up a Known Color Space
|
||||
@ -881,7 +960,7 @@ returns a color `color`.
|
||||
[missing]: #missing-components
|
||||
[powerless]: #powerless-components
|
||||
|
||||
## CSS-Converting a Color Space
|
||||
### CSS-Converting a Color Space
|
||||
|
||||
[css-converting]: #css-converting-a-color-space
|
||||
|
||||
@ -1147,9 +1226,10 @@ normalized channel value otherwise.
|
||||
* Otherwise, set `channel` to the result of [percent-converting] `channel`
|
||||
with a `min` and `max` defined by the `valid` channel range.
|
||||
|
||||
* If `valid` is a `lightness` channel, and `space` is not a [legacy color]
|
||||
space, set `channel` to the result of clamping the `channel` value between
|
||||
0 and 100, inclusive.
|
||||
* If this was (transitively) invoked from the global [`rgb()`, `lab()`,
|
||||
`lch()`, `oklab()`, `oklch()`, or `color()`] function and `valid` is a
|
||||
clamped channel, return the result of clamping `channel` to its native
|
||||
range.
|
||||
|
||||
* Return `channel`.
|
||||
|
||||
@ -1159,7 +1239,7 @@ normalized channel value otherwise.
|
||||
|
||||
[normalizing]: #normalizing-color-channels
|
||||
|
||||
This process accepts a list of `channels` to validate, and a [known color space]
|
||||
This process accepts a list of `channels` to validate and a [known color space]
|
||||
`space` to normalize against. It throws an error if any channel is invalid for
|
||||
the color space, or returns a normalized list of valid channels otherwise.
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
## Draft 1.3
|
||||
|
||||
* Handle empty subpath in "Resolving package exports" subprocedure.
|
||||
|
||||
## Draft 1.2
|
||||
|
||||
* Export `NodePackageImporter` type, and set `_NodePackageImporterBrand` to
|
||||
unknown.
|
||||
|
||||
## Draft 1.1
|
||||
|
||||
* Throw an error if `nodePackageImporter` is used in the browser or other
|
||||
environment without filesystem access.
|
||||
|
||||
* Remove specified order in the global import list, as users can specify the
|
||||
order within the `importers` option.
|
||||
|
||||
* Specify importer ordering for the Legacy API.
|
||||
|
||||
## Draft 1
|
||||
|
||||
* Initial draft
|
47
proposal/remove-feature-exists.md
Normal file
47
proposal/remove-feature-exists.md
Normal file
@ -0,0 +1,47 @@
|
||||
# `feature-exists` removal: Draft 1.0
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/3702))*
|
||||
|
||||
This proposal removes `feature-exists` in the built-in `sass:meta` module.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Background](#background)
|
||||
* [Summary](#summary)
|
||||
* [Deprecation Process](#deprecation-process)
|
||||
* [`feature-exists`](#feature-exists)
|
||||
|
||||
## Background
|
||||
|
||||
> This section is non-normative.
|
||||
|
||||
`feature-exists` is ill-defined as nothing specifies what each feature actually
|
||||
requires to be considered as supported. The shared `sass-spec` testsuite does
|
||||
not cover this either. Thus, no new feature identifier has been added in it
|
||||
for years.
|
||||
|
||||
New Sass features essentially fall into one of three categories:
|
||||
|
||||
1. New built-in functions or mixins, which are easy to detect using other
|
||||
`sass:meta` functions.
|
||||
2. Language-level features which are relatively easy to detect on their own
|
||||
(for example, first-class calc can be detected with `calc(1 + 1) == 2)`.
|
||||
3. New syntax which can't even be parsed in implementations that don't support
|
||||
it, for which feature detection isn't particularly useful anyway.
|
||||
|
||||
## Summary
|
||||
|
||||
> This section is non-normative.
|
||||
|
||||
The `feature-exists` function of `sass:meta` and its global alias will be
|
||||
removed without any direct replacement.
|
||||
|
||||
## Deprecation Process
|
||||
|
||||
The `feature-exists` function will be supported until the next major version
|
||||
with a deprecation warning.
|
||||
|
||||
### `feature-exists`
|
||||
|
||||
During the deprecation period, when invoking the `feature-exists` function,
|
||||
emit a deprecation warning named `feature-exists`.
|
@ -447,24 +447,54 @@ This function is also available as a global function named `hue()`.
|
||||
hwb($channels)
|
||||
```
|
||||
|
||||
* If `$channels` is not an unbracketed space-separated list, throw an error.
|
||||
* If `$channels` is an unbracketed slash-separated list:
|
||||
|
||||
* If `$channels` does not includes exactly three elements, throw an error.
|
||||
* If `$channels` doesn't have exactly two elements, throw an error.
|
||||
Otherwise, let `hwb` be the first element and `alpha` the second element.
|
||||
|
||||
* Let `hue` and `whiteness` be the first two elements of `$channels`
|
||||
* If either `hwb` or `alpha` is a [special variable string], return a plain
|
||||
CSS function string with the name `"hwb"` and the argument `$channels`.
|
||||
|
||||
* If the third element of `$channels` has preserved its status as
|
||||
two slash-separated numbers:
|
||||
* If `hwb` is not an unbracketed space-separated list, throw an error.
|
||||
|
||||
* Let `blackness` be the number before the slash and `alpha` the number
|
||||
after the slash.
|
||||
* If the first element of `hwb` is an unquoted string which is
|
||||
case-insensitively equal to `from`, return a plain CSS function string
|
||||
with the name `"hwb"` and the argument `$channels`.
|
||||
|
||||
* Otherwise:
|
||||
* If `hwb` has more than three elements, throw an error.
|
||||
|
||||
* Let `blackness` be the third element of `$channels`.
|
||||
* If `hwb` has fewer than three elements:
|
||||
|
||||
* Call `hwb()` with `hue`, `whiteness`, `blackness`, and `alpha` (if it's
|
||||
defined) as arguments and return the result.
|
||||
* If any element of `hwb` is a special variable string, return a plain
|
||||
CSS function string with the name `"hwb"` and the argument `$channels`.
|
||||
|
||||
* Otherwise, throw an error.
|
||||
|
||||
* Let `hue`, `whiteness`, and `blackness` be the three elements of `hwb`.
|
||||
|
||||
* Call `hwb()` with `hue`, `whiteness`, `blackness`, and `alpha` as
|
||||
arguments and return the result.
|
||||
|
||||
* Otherwise, If `$channels` is not an unbracketed space-separated list, throw an error.
|
||||
|
||||
* If `$channels` does not includes exactly three elements, throw an error.
|
||||
|
||||
* Let `hue` and `whiteness` be the first two elements of `$channels`
|
||||
|
||||
* If the third element of `$channels` has preserved its status as
|
||||
two slash-separated numbers:
|
||||
|
||||
* Let `blackness` be the number before the slash and `alpha` the number
|
||||
after the slash.
|
||||
|
||||
* Otherwise:
|
||||
|
||||
* Let `blackness` be the third element of `$channels`.
|
||||
|
||||
* Call `hwb()` with `hue`, `whiteness`, `blackness`, and `alpha` (if it's
|
||||
defined) as arguments and return the result.
|
||||
|
||||
[special variable string]: ../functions.md#special-variable-string
|
||||
|
||||
### `ie-hex-str()`
|
||||
|
||||
|
@ -72,6 +72,7 @@ This function is also available as a global function named `list-separator()`.
|
||||
slash($elements...)
|
||||
```
|
||||
|
||||
* Emit a deprecation warning.
|
||||
* If `$elements` contains zero or one values, throw an error.
|
||||
* Return an unbracketed slash-separated list containing `$elements`.
|
||||
|
||||
|
@ -98,6 +98,13 @@ message InboundMessage {
|
||||
// generating this ID and ensuring that it's unique across all importers
|
||||
// registered for this compilation.
|
||||
uint32 file_importer_id = 3;
|
||||
|
||||
// The [Node.js package importer], which is a built-in Package Importer
|
||||
// with an associated `entry_point_directory` that resolves `pkg:` URLs
|
||||
// using the standards and conventions of the Node ecosystem.
|
||||
//
|
||||
// [Node.js package importer]: https://github.com/sass/sass/tree/main/spec/modules.md#node-package-importer
|
||||
NodePackageImporter node_package_importer = 5;
|
||||
}
|
||||
|
||||
// The set of URL schemes that are considered *non-canonical* for this
|
||||
@ -148,6 +155,9 @@ message InboundMessage {
|
||||
|
||||
// Whether to emit a `@charset`/BOM for non-ASCII stylesheets.
|
||||
bool charset = 13;
|
||||
|
||||
// Whether to silently suppresses all `LogEvent`s.
|
||||
bool silent = 14;
|
||||
}
|
||||
|
||||
// A response indicating the result of canonicalizing an imported URL.
|
||||
@ -1018,3 +1028,16 @@ enum CalculationOperator {
|
||||
// The division operator.
|
||||
DIVIDE = 3;
|
||||
}
|
||||
|
||||
// The built-in Node.js Package Importer, which is a Package Importer that
|
||||
// resolves using the standards and conventions of the Node.js ecosystem. It
|
||||
// enables a `pkg:` URL scheme for usage with `@use` that directs an
|
||||
// implementation to resolve a URL within a dependency.
|
||||
message NodePackageImporter {
|
||||
// The absolute path to associate with the Node Package Importer, with
|
||||
// semantics identical to the [entryPointDirectory option] in the JavaScript
|
||||
// API.
|
||||
//
|
||||
// [entryPointDirectory option]: https://sass-lang.com/documentation/js-api/classes/NodePackageImporter.html#constructor
|
||||
string entry_point_directory = 1;
|
||||
}
|
||||
|
@ -291,7 +291,8 @@ plain CSS function named `"rgb"` that function is named `"rgba"` instead.
|
||||
* Call `rgb()` with `red`, `green`, `blue`, and `alpha` as arguments and
|
||||
return the result.
|
||||
|
||||
* If `$channels` is not an unbracketed space-separated list, throw an error.
|
||||
* Otherwise, if `$channels` is not an unbracketed space-separated list, throw
|
||||
an error.
|
||||
|
||||
* If the first element of `$channels` is an unquoted string which is
|
||||
case-insensitively equal to `from`, return a plain CSS function string
|
||||
@ -425,7 +426,8 @@ plain CSS function named `"hsl"` that function is named `"hsla"` instead.
|
||||
* Call `hsl()` with `hue`, `saturation`, `lightness`, and `alpha` as
|
||||
arguments and return the result.
|
||||
|
||||
* If `$channels` is not an unbracketed space-separated list, throw an error.
|
||||
* Otherwise, if `$channels` is not an unbracketed space-separated list, throw
|
||||
an error.
|
||||
|
||||
* If the first element of `$channels` is an unquoted string which is
|
||||
case-insensitively equal to `from`, return a plain CSS function string
|
||||
|
@ -12,11 +12,17 @@ import {Options, StringOptions} from './options';
|
||||
|
||||
* [Types](#types)
|
||||
* [`CompileResult`](#compileresult)
|
||||
* [`Compiler`](#compiler)
|
||||
* [`dispose()`](#dispose)
|
||||
* [`AsyncCompiler`](#asynccompiler)
|
||||
* [`dispose()`](#dispose-1)
|
||||
* [Functions](#functions)
|
||||
* [`compile`](#compile)
|
||||
* [`compileAsync`](#compileasync)
|
||||
* [`compileString`](#compilestring)
|
||||
* [`compileStringAsync`](#compilestringasync)
|
||||
* [`initCompiler`](#initcompiler)
|
||||
* [`initAsyncCompiler`](#initasynccompiler)
|
||||
|
||||
## Types
|
||||
|
||||
@ -34,12 +40,109 @@ export interface CompileResult {
|
||||
}
|
||||
```
|
||||
|
||||
### `Compiler`
|
||||
|
||||
The class returned by creating a synchronous compiler with [`initCompiler()`].
|
||||
If the class is directly constructed (as opposed to the Compiler being created
|
||||
via `initCompiler`), throw an error.
|
||||
|
||||
Only synchronous compilation methods [`compile()`] and [`compileString()`] must
|
||||
be included, and must have identical semantics to the [Sass interface].
|
||||
|
||||
[`initCompiler()`]: #initcompiler
|
||||
[`compile()`]: #compile
|
||||
[`compilestring()`]: #compilestring
|
||||
[Sass interface]: ./index.d.ts.md
|
||||
|
||||
```ts
|
||||
export class Compiler {
|
||||
private constructor();
|
||||
compile(path: string, options?: Options<'sync'>): CompileResult;
|
||||
|
||||
compileString(source: string, options?: StringOptions<'sync'>): CompileResult;
|
||||
```
|
||||
|
||||
#### `dispose()`
|
||||
|
||||
When `dispose` is invoked on a Compiler:
|
||||
|
||||
* Any subsequent invocations of `compile` and `compileString` must throw an
|
||||
error.
|
||||
|
||||
```ts
|
||||
dispose(): void;
|
||||
}
|
||||
```
|
||||
|
||||
### `AsyncCompiler`
|
||||
|
||||
The object returned by creating an asynchronous compiler with
|
||||
[`initAsyncCompiler()`]. If the class is directly constructed (as opposed to the
|
||||
AsyncCompiler being created via `initAsyncCompiler`), throw an error.
|
||||
|
||||
Only asynchronous compilation methods [`compileAsync()`] and
|
||||
[`compileStringAsync()`] must be included, and must have identical semantics to
|
||||
the [Sass interface].
|
||||
|
||||
[`initAsyncCompiler()`]: #initasynccompiler
|
||||
[`compileasync()`]: #compileasync
|
||||
[`compilestringasync()`]: #compilestringasync
|
||||
|
||||
```ts
|
||||
export class AsyncCompiler {
|
||||
private constructor();
|
||||
compileAsync(
|
||||
path: string,
|
||||
options?: Options<'async'>
|
||||
): Promise<CompileResult>;
|
||||
|
||||
compileStringAsync(
|
||||
source: string,
|
||||
options?: StringOptions<'async'>
|
||||
): Promise<CompileResult>;
|
||||
```
|
||||
|
||||
#### `dispose()`
|
||||
|
||||
When `dispose` is invoked on an Async Compiler:
|
||||
|
||||
* Any subsequent invocations of `compileAsync` and `compileStringAsync` must
|
||||
throw an error.
|
||||
|
||||
* Any compilations that have not yet been settled must be allowed to settle, and
|
||||
not be cancelled.
|
||||
|
||||
* Resolves a Promise when all compilations have been settled, and disposal is
|
||||
complete.
|
||||
|
||||
```ts
|
||||
dispose(): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### `compile`
|
||||
|
||||
Compiles the Sass file at `path`:
|
||||
|
||||
* If any item in `options.importers` is an instance of the `NodePackageImporter`
|
||||
class:
|
||||
|
||||
* If no filesystem is available, throw an error.
|
||||
|
||||
> This primarily refers to a browser environment, but applies to other
|
||||
> sandboxed JavaScript environments as well.
|
||||
|
||||
* Let `pkgImporter` be a [Node Package Importer] with an associated
|
||||
`entryPointURL` of the absolute file URL for the object's
|
||||
[`entryPointDirectory`].
|
||||
|
||||
[Node Package Importer]: ../modules.md#node-package-importer
|
||||
[`entryPointDirectory`]: importer.d.ts.md#constructor
|
||||
|
||||
* Replace the item with `pkgImporter` in a copy of `options.importers`.
|
||||
|
||||
* If any object in `options.importers` has both `findFileUrl` and `canonicalize`
|
||||
fields, throw an error.
|
||||
|
||||
@ -91,6 +194,20 @@ export function compileAsync(
|
||||
|
||||
Compiles the Sass `source`:
|
||||
|
||||
* If any item in `options.importers` is an instance of the `NodePackageImporter`
|
||||
class:
|
||||
|
||||
* If no filesystem is available, throw an error.
|
||||
|
||||
> This primarily refers to a browser environment, but applies to other
|
||||
> sandboxed JavaScript environments as well.
|
||||
|
||||
* Let `pkgImporter` be a [Node Package Importer] with an associated
|
||||
`entryPointURL` of the absolute file URL for the object's
|
||||
[`entryPointDirectory`].
|
||||
|
||||
* Replace the item with `pkgImporter` in a copy of `options.importers`.
|
||||
|
||||
* If `options.importer` or any object in `options.importers` has both
|
||||
`findFileUrl` and `canonicalize` fields, throw an error.
|
||||
|
||||
@ -146,3 +263,23 @@ export function compileStringAsync(
|
||||
options?: StringOptions<'async'>
|
||||
): Promise<CompileResult>;
|
||||
```
|
||||
|
||||
### `initCompiler`
|
||||
|
||||
Returns a synchronous [Compiler].
|
||||
|
||||
[Compiler]: #compiler
|
||||
|
||||
```ts
|
||||
export function initCompiler(): Compiler;
|
||||
```
|
||||
|
||||
### `initAsyncCompiler`
|
||||
|
||||
Resolves with an [Async Compiler].
|
||||
|
||||
[Async Compiler]: #asynccompiler
|
||||
|
||||
```ts
|
||||
export function initAsyncCompiler(): Promise<AsyncCompiler>;
|
||||
```
|
||||
|
@ -15,6 +15,9 @@ import {PromiseOr} from './util/promise_or';
|
||||
* [`FileImporter`](#fileimporter)
|
||||
* [`Importer`](#importer)
|
||||
* [`nonCanonicalScheme`](#noncanonicalscheme)
|
||||
* [`NodePackageImporter`](#nodepackageimporter)
|
||||
* [`entryPointDirectory`](#entrypointdirectory)
|
||||
* [Constructor](#constructor)
|
||||
* [`ImporterResult`](#importerresult)
|
||||
|
||||
## Types
|
||||
@ -161,6 +164,56 @@ nonCanonicalScheme?: string | string[];
|
||||
} // Importer
|
||||
```
|
||||
|
||||
### `NodePackageImporter`
|
||||
|
||||
```ts
|
||||
declare const nodePackageImporterKey: unique symbol;
|
||||
|
||||
export class NodePackageImporter {
|
||||
/** Used to distinguish this type from any arbitrary object. */
|
||||
private readonly [nodePackageImporterKey]: true;
|
||||
```
|
||||
|
||||
#### `entryPointDirectory`
|
||||
|
||||
This is a private, spec-only string property that's used to determine the value
|
||||
of the `parentURL` parameter to `PACKAGE_RESOLVE` function from the [Node.js
|
||||
Resolution Algorithm].
|
||||
|
||||
[Node.js Resolution Algorithm]: https://nodejs.org/api/esm.html#resolution-algorithm-specification
|
||||
|
||||
#### Constructor
|
||||
|
||||
Creates a `NodePackageImporter`:
|
||||
|
||||
* If no filesystem is available, throw an error.
|
||||
|
||||
> This primarily refers to a browser environment, but applies to other
|
||||
> sandboxed JavaScript environments as well.
|
||||
|
||||
* If `entryPointDirectory` is not `undefined`, set the `entryPointDirectory`
|
||||
property to its value.
|
||||
|
||||
* Otherwise, if the path to the first JavaScript file that was loaded from the
|
||||
filesystem exists, set the `entryPointDirectory` property to the directory
|
||||
containing that path.
|
||||
|
||||
> In Node.js, this can be accessed using `require.main.filename` if the
|
||||
> entrypoint is CommonJS, but otherwise must be determined using
|
||||
> `process.argv[1]`. See nodejs/node#49440.
|
||||
|
||||
* Otherwise, throw an error.
|
||||
|
||||
* Return `this`.
|
||||
|
||||
```ts
|
||||
constructor(entryPointDirectory?: string);
|
||||
```
|
||||
|
||||
```ts
|
||||
} // NodePackageImporter
|
||||
```
|
||||
|
||||
### `ImporterResult`
|
||||
|
||||
```ts
|
||||
|
@ -40,11 +40,15 @@ proposals.
|
||||
|
||||
```ts
|
||||
export {
|
||||
AsyncCompiler,
|
||||
CompileResult,
|
||||
Compiler,
|
||||
compile,
|
||||
compileAsync,
|
||||
compileString,
|
||||
compileStringAsync,
|
||||
initCompiler,
|
||||
initAsyncCompiler,
|
||||
} from './compile';
|
||||
export {Exception} from './exception';
|
||||
export {
|
||||
@ -52,6 +56,7 @@ export {
|
||||
FileImporter,
|
||||
Importer,
|
||||
ImporterResult,
|
||||
NodePackageImporter,
|
||||
} from './importer';
|
||||
export {Logger, SourceSpan, SourceLocation} from './logger';
|
||||
export {
|
||||
|
@ -4,6 +4,7 @@
|
||||
import {Logger} from '../logger';
|
||||
import {LegacyImporter} from './importer';
|
||||
import {LegacyFunction} from './function';
|
||||
import {NodePackageImporter} from '../importer';
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
@ -27,6 +28,7 @@ import {LegacyFunction} from './function';
|
||||
* [`quietDeps`](#quietdeps)
|
||||
* [`verbose`](#verbose)
|
||||
* [`logger`](#logger)
|
||||
* [`pkgImporter`](#pkgimporter)
|
||||
* [`LegacyFileOptions`](#legacyfileoptions)
|
||||
* [`LegacyStringOptions`](#legacystringoptions)
|
||||
* [`LegacyOptions`](#legacyoptions)
|
||||
@ -173,6 +175,19 @@ doesn't have `warn` or `debug` fields.
|
||||
logger?: Logger;
|
||||
```
|
||||
|
||||
#### `pkgImporter`
|
||||
|
||||
If set to an instance of [NodePackageImporter], the compiler will use it as the
|
||||
specified built-in package importer to resolve any URL with the `pkg:` scheme.
|
||||
This importer will be checked immediately before the existing legacy importer
|
||||
logic, and if it returns `null`, the legacy importer logic will be invoked.
|
||||
|
||||
[NodePackageImporter]: ../importer.d.ts.md#nodepackageimporter
|
||||
|
||||
```ts
|
||||
pkgImporter?: NodePackageImporter;
|
||||
```
|
||||
|
||||
```ts
|
||||
} // LegacySharedOptions
|
||||
```
|
||||
|
@ -6,7 +6,7 @@
|
||||
> [compile API]: compile.d.ts.md
|
||||
|
||||
```ts
|
||||
import {FileImporter, Importer} from './importer';
|
||||
import {FileImporter, Importer, NodePackageImporter} from './importer';
|
||||
import {Logger} from './logger';
|
||||
import {Value} from './value';
|
||||
import {PromiseOr} from './util/promise_or';
|
||||
@ -165,12 +165,14 @@ functions?: Record<string, CustomFunction<sync>>;
|
||||
|
||||
#### `importers`
|
||||
|
||||
The list of [custom importers] to use to resolve file loads.
|
||||
The list of [custom importers] or [package importers] to use to resolve file
|
||||
loads.
|
||||
|
||||
[custom importers]: importer.d.ts.md
|
||||
[package importers]: importer.d.ts.md
|
||||
|
||||
```ts
|
||||
importers?: (Importer<sync> | FileImporter<sync>)[];
|
||||
importers?: (Importer<sync> | FileImporter<sync> | NodePackageImporter)[];
|
||||
```
|
||||
|
||||
#### `loadPaths`
|
||||
|
242
spec/modules.md
242
spec/modules.md
@ -12,6 +12,8 @@
|
||||
* [Built-In Module](#built-in-module)
|
||||
* [Importer](#importer)
|
||||
* [Filesystem Importer](#filesystem-importer)
|
||||
* [Package Importers](#package-importers)
|
||||
* [Node Package Importer](#node-package-importer)
|
||||
* [Global Importer List](#global-importer-list)
|
||||
* [Basename](#basename)
|
||||
* [Dirname](#dirname)
|
||||
@ -23,6 +25,13 @@
|
||||
* [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)
|
||||
* [Node Package Importer Semantics](#node-package-importer-semantics)
|
||||
* [Node Algorithm for Resolving a `pkg:` URL](#node-algorithm-for-resolving-a-pkg-url)
|
||||
* [Resolving a package name](#resolving-a-package-name)
|
||||
* [Resolving the root directory for a package](#resolving-the-root-directory-for-a-package)
|
||||
* [Resolving package exports](#resolving-package-exports)
|
||||
* [Resolving package root values](#resolving-package-root-values)
|
||||
* [Export Load Paths](#export-load-paths)
|
||||
|
||||
## Definitions
|
||||
|
||||
@ -211,6 +220,81 @@ named `string`:
|
||||
|
||||
[parsing a URL]: https://url.spec.whatwg.org/#concept-url-parser
|
||||
|
||||
### Package Importers
|
||||
|
||||
This proposal defines the requirements for Package Importers written by users or
|
||||
provided by implementations. It is a type of [Importer] and, in addition to the
|
||||
standard requirements for importers, it must handle only non-canonical URLs that:
|
||||
|
||||
* have the scheme `pkg`, and
|
||||
* whose path begins with a package name, and
|
||||
* optionally followed by a path, with path segments separated with a forward
|
||||
slash.
|
||||
|
||||
The package name will often be the first path segment, but the importer may take
|
||||
into account any conventions in the environment. For instance, Node supports
|
||||
scoped package names, which start with `@` followed by 2 path segments. Note
|
||||
that package names that contain non-alphanumeric characters may be less portable
|
||||
across different package importers.
|
||||
|
||||
Package Importers must reject the following patterns:
|
||||
|
||||
* A URL whose path begins with `/`.
|
||||
* A URL with non-empty/null username, password, host, port, query, or fragment.
|
||||
|
||||
[importer]: #importer
|
||||
|
||||
### Node Package Importer
|
||||
|
||||
The Node Package Importer is an implementation of a [Package Importer] using the
|
||||
standards and conventions of the Node ecosystem. It has an associated absolute
|
||||
`file:` URL named `entryPointURL`.
|
||||
|
||||
When the Node Package Importer is invoked with a string named `string`:
|
||||
|
||||
* If `string` is a relative URL, return null.
|
||||
|
||||
* Let `url` be the result of [parsing `string` as a URL][parsing a URL]. If this
|
||||
returns a failure, throw that failure.
|
||||
|
||||
* If `url`'s scheme is not `pkg:`, return null.
|
||||
|
||||
* If `url`'s path begins with a `/` or is empty, throw an error.
|
||||
|
||||
* If `url` contains a username, password, host, port, query, or fragment, throw
|
||||
an error.
|
||||
|
||||
* Let `sourceFile` be the canonical URL of the [current source file] that
|
||||
contained the load.
|
||||
|
||||
* If `sourceFile`'s scheme is `file:`, let `baseURL` be `sourceFile`.
|
||||
|
||||
* Otherwise, let `baseURL` be `entryPointURL`.
|
||||
|
||||
* Let `resolved` be the result of [resolving a `pkg:` URL as Node] with `url` and
|
||||
`baseURL`.
|
||||
|
||||
* If `resolved` is null, return null.
|
||||
|
||||
* Let `text` be the contents of the file at `resolved`.
|
||||
|
||||
* Let `syntax` be:
|
||||
|
||||
* "scss" if `resolved` ends in `.scss`.
|
||||
|
||||
* "indented" if `resolved` ends in `.sass`.
|
||||
|
||||
* "css" if `resolved` ends in `.css`.
|
||||
|
||||
> The algorithm for [resolving a `pkg:` URL as Node] guarantees that
|
||||
> `resolved` will have one of these extensions.
|
||||
|
||||
* Return `text`, `syntax`, and `resolved`.
|
||||
|
||||
[Package Importer]: #package-importers
|
||||
[current source file]: ./spec.md#current-source-file
|
||||
[resolving a `pkg:` URL as Node]: #node-algorithm-for-resolving-a-pkg-url
|
||||
|
||||
### Global Importer List
|
||||
|
||||
The *global importer list* is a list of importers that's set for the entire
|
||||
@ -322,7 +406,6 @@ null.
|
||||
|
||||
* Return null.
|
||||
|
||||
[current source file]: spec.md#current-source-file
|
||||
[parsing]: syntax.md#parsing-text
|
||||
|
||||
### Resolving a `file:` URL
|
||||
@ -506,3 +589,160 @@ and returns a member of type `type` or null.
|
||||
> implementation's host language API.
|
||||
|
||||
* Otherwise, return null.
|
||||
|
||||
### Node Package Importer Semantics
|
||||
|
||||
#### Node Algorithm for Resolving a `pkg:` URL
|
||||
|
||||
This algorithm takes a URL with scheme `pkg:` named `url`, and a URL `baseURL`.
|
||||
It returns a canonical `file:` URL or null.
|
||||
|
||||
* Let `fullPath` be `url`'s path.
|
||||
|
||||
* Let `packageName` be the result of [resolving a package name] with `fullPath`,
|
||||
and `subpath` be `fullPath` without the `packageName`.
|
||||
|
||||
* Let `packageRoot` be the result of [resolving the root directory for a
|
||||
package] with `packageName` and `baseURL`.
|
||||
|
||||
* If a `package.json` file does not exist at `packageRoot`, throw an error.
|
||||
|
||||
* Let `packageManifest` be the result of parsing the `package.json` file at
|
||||
`packageRoot` as [JSON].
|
||||
|
||||
* Let `resolved` be the result of [resolving package exports] with
|
||||
`packageRoot`, `subpath`, and `packageManifest`.
|
||||
|
||||
* If `resolved` has the scheme `file:` and an extension of `sass`, `scss` or
|
||||
`css`, return it.
|
||||
|
||||
* Otherwise, if `resolved` is not null, throw an error.
|
||||
|
||||
* If `subpath` is empty, return the result of [resolving package root values].
|
||||
|
||||
* Let `resolved` be `subpath` resolved relative to `packageRoot`.
|
||||
|
||||
* Return the result of [resolving a `file:` URL] with `resolved`.
|
||||
|
||||
[Resolving package exports]: #resolving-package-exports
|
||||
[resolving package root values]: #resolving-package-root-values
|
||||
[resolving a package name]: #resolving-a-package-name
|
||||
[JSON]: https://datatracker.ietf.org/doc/html/rfc8259
|
||||
[resolving the root directory for a package]: #resolving-the-root-directory-for-a-package
|
||||
[resolving a `file:` URL]: #resolving-a-file-url
|
||||
|
||||
#### Resolving a package name
|
||||
|
||||
This algorithm takes a string, `path`, and returns the portion that identifies
|
||||
the Node package.
|
||||
|
||||
* If `path` starts with `@`, it is a scoped package. Return the first 2 [URL path
|
||||
segments], including the separating `/`.
|
||||
|
||||
* Otherwise, return the first URL path segment.
|
||||
|
||||
#### Resolving the root directory for a package
|
||||
|
||||
This algorithm takes a string, `packageName`, and an absolute URL `baseURL`, and
|
||||
returns an absolute URL to the root directory for the most proximate installed
|
||||
`packageName`.
|
||||
|
||||
* Return the result of `PACKAGE_RESOLVE(packageName, baseURL)` as defined in
|
||||
the [Node resolution algorithm specification].
|
||||
|
||||
[Node resolution algorithm specification]: https://nodejs.org/api/esm.html#resolution-algorithm-specification
|
||||
|
||||
#### Resolving package exports
|
||||
|
||||
This algorithm takes a package.json value `packageManifest`, a directory URL
|
||||
`packageRoot` and a relative URL path `subpath`. It returns a file URL or null.
|
||||
|
||||
* Let `exports` be the value of `packageManifest.exports`.
|
||||
|
||||
* If `exports` is undefined, return null.
|
||||
|
||||
* Let `subpathVariants` be the result of [Export load paths] with `subpath`.
|
||||
|
||||
* Let `resolvedPaths` be a list of the results of calling
|
||||
`PACKAGE_EXPORTS_RESOLVE(packageRoot, subpathVariant, exports, ["sass",
|
||||
"style"])` as defined in the [Node resolution algorithm specification], with
|
||||
each `subpathVariants` as `subpathVariant`.
|
||||
|
||||
> The PACKAGE_EXPORTS_RESOLVE algorithm always includes a `default` condition,
|
||||
> so one does not have to be passed here.
|
||||
|
||||
* If `resolvedPaths` contains more than one resolved URL, throw an error.
|
||||
|
||||
* If `resolvedPaths` contains exactly one resolved URL, return it.
|
||||
|
||||
* If `subpath` has an extension, return null.
|
||||
|
||||
* Let `subpathIndex` be `subpath` + `"/index"`.
|
||||
|
||||
* Let `subpathIndexVariants` be the result of [Export load paths] with `subpathIndex`.
|
||||
|
||||
* Let `resolvedIndexPaths` be a list of the results of calling
|
||||
`PACKAGE_EXPORTS_RESOLVE(packageRoot, subpathVariant, exports, ["sass",
|
||||
"style"])` as defined in the [Node resolution algorithm specification], with
|
||||
each `subpathIndexVariants` as `subpathVariant`.
|
||||
|
||||
* If `resolvedIndexPaths` contains more than one resolved URL, throw an error.
|
||||
|
||||
* If `resolvedIndexPaths` contains exactly one resolved URL, return it.
|
||||
|
||||
* Return null.
|
||||
|
||||
> Where possible in Node, implementations can use [resolve.exports] which
|
||||
> exposes the Node resolution algorithm, allowing for per-path custom
|
||||
> conditions, and without needing filesystem access.
|
||||
|
||||
[Export load paths]: #export-load-paths
|
||||
[resolve.exports]: https://github.com/lukeed/resolve.exports
|
||||
|
||||
#### Resolving package root values
|
||||
|
||||
This algorithm takes a string `packagePath`, which is the root directory for a
|
||||
package, and `packageManifest`, which is the contents of that package's
|
||||
`package.json` file, and returns a file URL.
|
||||
|
||||
* Let `sassValue` be the value of `sass` in `packageManifest`.
|
||||
|
||||
* If `sassValue` is a relative path with an extension of `sass`, `scss` or
|
||||
`css`:
|
||||
|
||||
* Return the canonicalized `file:` URL for `${packagePath}/${sassValue}`.
|
||||
|
||||
* Let `styleValue` be the value of `style` in `packageManifest`.
|
||||
|
||||
* If `styleValue` is a relative path with an extension of `sass`, `scss` or
|
||||
`css`:
|
||||
|
||||
* Return the canonicalized `file:` URL for `${packagePath}/${styleValue}`.
|
||||
|
||||
* Otherwise return the result of [resolving a `file:` URL for extensions] with
|
||||
`packagePath + "/index"`.
|
||||
|
||||
[resolving a `file:` URL for extensions]: #resolving-a-file-url-for-extensions
|
||||
|
||||
[URL path segments]: https://url.spec.whatwg.org/#url-path-segment
|
||||
|
||||
#### Export Load Paths
|
||||
|
||||
This algorithm takes a relative URL path `subpath` and returns a list of
|
||||
potential subpaths, resolving for partials and file extensions.
|
||||
|
||||
* Let `paths` be a list.
|
||||
|
||||
* If `subpath` ends in `.scss`, `.sass`, or `.css`:
|
||||
|
||||
* Add `subpath` to `paths`.
|
||||
|
||||
* Otherwise, add `subpath`, `subpath` + `.scss`, `subpath` + `.sass`, and
|
||||
`subpath` + `.css` to `paths`.
|
||||
|
||||
* If `subpath`'s [basename] does not start with `_`, for each `item` in
|
||||
`paths`, prepend `"_"` to the basename, and add to `paths`.
|
||||
|
||||
* Return `paths`.
|
||||
|
||||
[basename]: #basename
|
||||
|
@ -5,12 +5,13 @@
|
||||
* [Definitions](#definitions)
|
||||
* [Source File](#source-file)
|
||||
* [Vendor Prefix](#vendor-prefix)
|
||||
* [Grammar](#grammar)
|
||||
* [Syntax](#syntax-1)
|
||||
* [`InterpolatedIdentifier`](#interpolatedidentifier)
|
||||
* [`InterpolatedUrl`](#interpolatedurl)
|
||||
* [`Name`](#name)
|
||||
* [`SpecialFunctionExpression`](#specialfunctionexpression)
|
||||
* [`PseudoSelector`](#pseudoselector)
|
||||
* [`ProductExpression`](#productexpression)
|
||||
* [Procedures](#procedures)
|
||||
* [Parsing Text](#parsing-text)
|
||||
* [Parsing Text as CSS](#parsing-text-as-css)
|
||||
@ -38,7 +39,23 @@ points followed by another U+002D. An identifier only has a vendor prefix if the
|
||||
final U+002D is followed by additional text. This additional text is referred to
|
||||
as the *unprefixed identifier*.
|
||||
|
||||
## Grammar
|
||||
## Syntax
|
||||
|
||||
Unless otherwise specified, if a grammar production's value is parsed as a
|
||||
single other named Sass production, then the AST does not contain a
|
||||
representation of the intermediate production.
|
||||
|
||||
> For example, suppose we have the productions:
|
||||
>
|
||||
> <x><pre>
|
||||
> **Foo** ::= Bar '*'?
|
||||
> **Bar** ::= \<ident-token>
|
||||
> </pre></x>
|
||||
>
|
||||
> Parsing `"ident*"` creates an AST that contains a `Foo` that contains a `Bar`.
|
||||
> But parsing `"ident"` alone creates an AST that only contains a `Bar`—the
|
||||
> `Foo` wrapper is removed. (The `Bar` wrapper is *not* removed because
|
||||
> `<ident-token>` is a CSS production, not a Sass production.)
|
||||
|
||||
### `InterpolatedIdentifier`
|
||||
|
||||
@ -121,6 +138,12 @@ followed by a parenthesis, it must be parsed as a `SelectorPseudo` or an
|
||||
No whitespace is allowed anywhere in a `PseudoSelector` except within
|
||||
parentheses.
|
||||
|
||||
### `ProductExpression`
|
||||
|
||||
<x><pre>
|
||||
**ProductExpression** ::= (ProductExpression ('*' | '%'))? UnaryPlusExpression
|
||||
</pre></x>
|
||||
|
||||
## Procedures
|
||||
|
||||
### Parsing Text
|
||||
@ -219,7 +242,6 @@ modifications. The following productions should produce errors:
|
||||
|
||||
* All SassScript operations *except for*:
|
||||
|
||||
* `/`
|
||||
* `not`
|
||||
* `or`
|
||||
* `and`
|
||||
@ -235,7 +257,7 @@ modifications. The following productions should produce errors:
|
||||
|
||||
* Uses or declarations of Sass variables.
|
||||
|
||||
* `//`-style ("silent") comments.
|
||||
* `//`-style ("silent") comments outside of an expression context.
|
||||
|
||||
In addition, some productions should be parsed differently than they would be in
|
||||
SCSS:
|
||||
@ -249,10 +271,9 @@ SCSS:
|
||||
* The tokens `not`, `or`, `and`, and `null` should be parsed as unquoted
|
||||
strings.
|
||||
|
||||
> The `/` operation should be parsed as normal. Because variables,
|
||||
> parentheses, functions that return numbers, and all other arithmetic
|
||||
> expressions are disallowed, it will always compile to slash-separated values
|
||||
> rather than performing division.
|
||||
* In an expression context, `//` is not parsed as a silent comment. Instead, two
|
||||
adjacent `/`s in a [`SlashListExpression`] may have no whitespace between
|
||||
them, so `//` is parsed as two slash separators in a slash-separated list.
|
||||
|
||||
### Consuming an Identifier
|
||||
|
||||
|
@ -13,12 +13,14 @@
|
||||
* [`Number`](#number)
|
||||
* [Procedures](#procedures)
|
||||
* [Evaluating a `FunctionCall` as a Calculation](#evaluating-a-functioncall-as-a-calculation)
|
||||
* [Adjusting Slash Precedence](#adjusting-slash-precedence)
|
||||
* [Evaluating an Expression as a Calculation Value](#evaluating-an-expression-as-a-calculation-value)
|
||||
* [Simplifying a Calculation](#simplifying-a-calculation)
|
||||
* [Simplifying a `CalculationValue`](#simplifying-a-calculationvalue)
|
||||
* [Semantics](#semantics)
|
||||
* [`FunctionExpression` and `Variable`](#functionexpression-and-variable)
|
||||
* [`SumExpression` and `ProductExpression`](#sumexpression-and-productexpression)
|
||||
* [`SlashListExpression`](#slashlistexpression)
|
||||
* [`SpaceListExpression`](#spacelistexpression)
|
||||
* [`ParenthesizedExpression`](#parenthesizedexpression)
|
||||
* [`InterpolatedIdentifier`](#interpolatedidentifier)
|
||||
@ -32,13 +34,13 @@ An expression is "calculation-safe" if it is one of:
|
||||
* A [`FunctionExpression`].
|
||||
* A `ParenthesizedExpression` whose contents is calculation-safe.
|
||||
* A `SumExpression` whose operands are calculation-safe.
|
||||
* A `ProductExpression` whose operator is `*` or `/` and whose operands are
|
||||
* A `ProductExpression` whose operator is `*` and whose operands are
|
||||
calculation-safe.
|
||||
* A `Number`.
|
||||
* A `Variable`.
|
||||
* An `InterpolatedIdentifier`.
|
||||
* An unbracketed `SpaceListExpression` with more than one element, whose
|
||||
elements are all calculation-safe.
|
||||
* An unbracketed `SpaceListExpression` or `SlashListExpression` with more than
|
||||
one element, whose elements are all calculation-safe.
|
||||
|
||||
[`FunctionExpression`]: ../functions.md#syntax
|
||||
|
||||
@ -152,13 +154,71 @@ and returns a number or a calculation.
|
||||
one or more `RestArgument`s, throw an error.
|
||||
|
||||
* Let `calc` be a calculation whose name is the lower-case value of `call`'s
|
||||
name and whose arguments are the result of evaluating each `Expression` in
|
||||
`call`'s `ArgumentInvocation` [as a calculation value].
|
||||
name and whose arguments are the result of "[adjusting slash precedence] in
|
||||
and then evaluating each `Expression`" in `call`'s `ArgumentInvocation` [as a
|
||||
calculation value].
|
||||
|
||||
[adjusting slash precedence]: #adjusting-slash-precedence
|
||||
[as a calculation value]: #evaluating-an-expression-as-a-calculation-value
|
||||
|
||||
* Return the result of [simplifying](#simplifying-a-calculation) `calc`.
|
||||
|
||||
### Adjusting Slash Precedence
|
||||
|
||||
This algorithm takes a calculation-safe expression `expression` and returns
|
||||
another calculation-safe expression with the precedence of
|
||||
[`SlashListExpression`]s adjusted to match division precedence.
|
||||
|
||||
[`SlashListExpression`]: list.md#slashlistexpression
|
||||
|
||||
* Return a copy of `expression` except, for each `SlashListExpression`:
|
||||
|
||||
* Let `left` be the first element of the list.
|
||||
|
||||
* For each remaining element `right`:
|
||||
|
||||
* If `left` and `right` are both `SumExpression`s:
|
||||
|
||||
* Let `last-left` be the last operand of `left` and `first-right` the
|
||||
first operand of `right`.
|
||||
|
||||
* Set `left` to a `SumExpression` that begins with all operands and
|
||||
operators of `left` except `last-left`, followed by a
|
||||
`SlashListExpression` with elements `last-left` and `first-right`,
|
||||
followed by all operators and operands of `right` except `first-right`.
|
||||
|
||||
> For example, `slash-list(1 + 2, 3 + 4)` becomes `1 + (2 / 3) + 4`.
|
||||
|
||||
* Otherwise, if `left` is a `SumExpression`:
|
||||
|
||||
* Let `last-left` be the last operand of `left`.
|
||||
|
||||
* Set `left` to a `SumExpression` that begins with all operands and
|
||||
operators of `left` except `last-left`, followed by a
|
||||
`SlashListExpression` with elements `last-left` and `right`.
|
||||
|
||||
> For example, `slash-list(1 + 2, 3)` becomes `1 + (2 / 3)`.
|
||||
|
||||
* Otherwise, if `right` is a `SumExpression` or a `ProductExpression`:
|
||||
|
||||
* Let `first-right` be the first operand of `right`.
|
||||
|
||||
* Set `left` to an expression of the same type as `right` that begins a
|
||||
`SlashListExpression` with elements `left` and `first-right`, followed
|
||||
by operators and operands of `right` except `first-right`.
|
||||
|
||||
> For example, `slash-list(1, 2 * 3)` becomes `(1 / 2) * 3`.
|
||||
|
||||
* Otherwise, if `left` is a slash-separated list, add `right` to the end.
|
||||
|
||||
* Otherwise, set `left` to a slash-separated list containing `left` and
|
||||
`right`.
|
||||
|
||||
* Replace each element in `left` with the result of adjusting slash precedence
|
||||
in that element.
|
||||
|
||||
* Replace the `SlashListExpression` with `left` in the returned expression.
|
||||
|
||||
### Evaluating an Expression as a Calculation Value
|
||||
|
||||
This algorithm takes an expression `expression` and returns a
|
||||
@ -497,6 +557,21 @@ To evaluate a `SumExpresssion` or a `ProductExpression` as a calculation value:
|
||||
|
||||
* Return `left`.
|
||||
|
||||
### `SlashListExpression`
|
||||
|
||||
To evaluate a `SlashListExpression` as a calculation value:
|
||||
|
||||
* Let `left` be the result of evaluating the first element of the list as a
|
||||
calculation value.
|
||||
|
||||
* For each remaining element `element`:
|
||||
|
||||
* Let `right` be the result of evaluating `element` as a calculation value.
|
||||
|
||||
* Set `left` to a `CalcOperation` with operator `"/"`, `left`, and `right`.
|
||||
|
||||
* Return `left`.
|
||||
|
||||
### `SpaceListExpression`
|
||||
|
||||
To evaluate a `SpaceListExpresssion` as a calculation value:
|
||||
|
@ -6,6 +6,9 @@
|
||||
* [List](#list)
|
||||
* [List Value](#list-value)
|
||||
* [Index](#index)
|
||||
* [Syntax](#syntax)
|
||||
* [Semantics](#semantics)
|
||||
* [`SlashListExpression`](#slashlistexpression)
|
||||
|
||||
## Definitions
|
||||
|
||||
@ -44,3 +47,36 @@ absolute value is larger than the length of that list.
|
||||
> * `"c"`: 3, -1
|
||||
|
||||
[integer]: number.md#integer
|
||||
|
||||
## Syntax
|
||||
|
||||
<x><pre>
|
||||
**BracketedListExpression** ::= '[' ContainedListExpression ']'
|
||||
**ContainedListExpression** ::= CommaListExpression ','?
|
||||
**CommaListExpression** ::= SlashListExpression (',' SlashListExpression)*
|
||||
**SlashListExpression** ::= SpaceListExpression (('/' SpaceListExpression?)* '/' SpaceListExpression)?
|
||||
**SpaceListExpression** ::= SumExpression+
|
||||
</pre></x>
|
||||
|
||||
Every pair of adjacent `/`s in a `SlashListExpression` must be separated by
|
||||
whitespace or comments, unless the stylesheet is being parsed as CSS.
|
||||
|
||||
> Note that `/` may *not* be used in single-element lists the way `,` is. That
|
||||
> is, `(foo,)` is valid, but `(foo/)` is not.
|
||||
>
|
||||
> This defines `/` to bind tighter than `,` but looser than space-separated
|
||||
> lists. This was chosen because most common uses of `/` in CSS conceptually
|
||||
> bind looser than space-separated values. The only exception is the [`font`
|
||||
> shorthand syntax], which is used much more rarely will still work (albeit with
|
||||
> an unintuitive SassScript representation) with a loose-binding `/`.
|
||||
>
|
||||
> [`font` shorthand syntax]: https://developer.mozilla.org/en-US/docs/Web/CSS/font
|
||||
|
||||
## Semantics
|
||||
|
||||
### `SlashListExpression`
|
||||
|
||||
To evaluate a `SlashListExpression`, evaluate each of its `SpaceListExpression`s
|
||||
and return a slash-separated list that contains each of the results in order. If
|
||||
any `/` isn't followed by a `SpaceListExpression`, use an empty unquoted string
|
||||
as its value instead.
|
||||
|
@ -14,7 +14,6 @@
|
||||
* [Exact Equality](#exact-equality)
|
||||
* [Fuzzy Equality](#fuzzy-equality)
|
||||
* [Integer](#integer)
|
||||
* [Potentially Slash-Separated Number](#potentially-slash-separated-number)
|
||||
* [Types](#types)
|
||||
* [Operations](#operations)
|
||||
* [Equality](#equality)
|
||||
@ -210,37 +209,6 @@ If `m` exists, we say that `n`'s *integer value* is the double that represents
|
||||
> To avoid ambiguity, specification text will generally use the term
|
||||
> "mathematical integer" when referring to the abstract mathematical objects.
|
||||
|
||||
### Potentially Slash-Separated Number
|
||||
|
||||
A Sass number may be *potentially slash-separated*. If it is, it is associated
|
||||
with two additional Sass numbers, the *original numerator* and the *original
|
||||
denominator*. A number that is not potentially slash-separated is known as
|
||||
*slash-free*.
|
||||
|
||||
A potentially slash-separated number is created when a `ProductExpression` with
|
||||
a `/` operator is evaluated and each operand is *syntactically* one of the
|
||||
following:
|
||||
|
||||
* a `Number`,
|
||||
* a [`FunctionCall`], or
|
||||
* a `ProductExpression` that can itself create potentially slash-separated
|
||||
numbers.
|
||||
|
||||
[`FunctionCall`]: ../functions.md#functioncall
|
||||
|
||||
If the result of evaluating the `ProductExpression` is a number, that number is
|
||||
potentially slash-separated if all of the following are true:
|
||||
|
||||
* the results of evaluating both operands were numbers, and
|
||||
* if either operand was a `FunctionCall`, it was [evaluated as a calculation]
|
||||
and its name was not `"abs"`, `"max"`, `"min"`, or `"round"`.
|
||||
|
||||
[evaluated as a calculation]: calculation.md#evaluating-a-functioncall-as-a-calculation
|
||||
|
||||
If both of these are true, the first operand is the original numerator of the
|
||||
potentially slash-separated number returned by the `/` operator, and the second
|
||||
is the original denominator.
|
||||
|
||||
## Types
|
||||
|
||||
The value type known as a *number* has three components:
|
||||
|
@ -106,6 +106,8 @@ function runLinkCheck(
|
||||
{pattern: /^https?:\/\/blogs\.msdn\.microsoft\.com(\/|$)/},
|
||||
// Link consistently fails within CI
|
||||
{pattern: /^https:\/\/runtime-keys\.proposal\.wintercg\.org(\/|$)/},
|
||||
// Stackoverflow links can get rate-limited on GitHub Actions
|
||||
{pattern: /^https:\/\/stackoverflow\.com(\/|$)/},
|
||||
],
|
||||
},
|
||||
(error, results) => {
|
||||
|
Loading…
Reference in New Issue
Block a user