mirror of
https://github.com/sass/sass.git
synced 2024-09-21 10:37:22 +00:00
Merge remote-tracking branch 'origin/main' into merge-main
This commit is contained in:
commit
d41ff1bb18
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with: {node-version: '${{ env.NODE_VERSION }}'}
|
||||
- run: npm ci
|
||||
- run: >
|
||||
@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with: {node-version: '${{ env.NODE_VERSION }}'}
|
||||
- run: npm ci
|
||||
- run: npm run toc-check
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with: {node-version: '${{ env.NODE_VERSION }}'}
|
||||
- run: npm ci
|
||||
- run: npm run link-check
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with: {node-version: '${{ env.NODE_VERSION }}'}
|
||||
- run: npm ci
|
||||
- run: npm run js-api-doc-check
|
||||
@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with: {node-version: '${{ env.NODE_VERSION }}'}
|
||||
- run: npm ci
|
||||
- run: npm run typedoc
|
||||
@ -72,7 +72,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: bufbuild/buf-setup-action@v1.26.1
|
||||
- uses: bufbuild/buf-setup-action@v1.28.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@8238a4103220c636f2dad328ead8a7c8dbe316a3
|
||||
uses: tj-actions/changed-files@25ef3926d147cd02fc7e931c1ef50772bbb0d25d
|
||||
with: {files: js-api-doc}
|
||||
|
||||
- name: Deploy
|
||||
|
@ -134,7 +134,7 @@ The situations we chose to warn for are:
|
||||
`futureDeprecations`.
|
||||
|
||||
In this scenario, the future deprecation will still be treated as fatal,
|
||||
but we want to warn users to prevent situtations where a user tries to
|
||||
but we want to warn users to prevent situations where a user tries to
|
||||
make every deprecation fatal and ends up including future ones too.
|
||||
|
||||
* an obsolete deprecation is passed to `fatalDeprecations`.
|
||||
@ -326,7 +326,7 @@ behavior of their global counterparts for compatibility reasons.
|
||||
|
||||
#### `slash-div`
|
||||
|
||||
Deprecation for treaing `/` as division.
|
||||
Deprecation for treating `/` as division.
|
||||
|
||||
Update the proposal for forward slash as a separator to say that it emits
|
||||
deprecation warnings with ID 'slash-div'.
|
||||
|
@ -1187,7 +1187,7 @@ Given a source file `file`, a [configuration](#configuration) `config`, and an
|
||||
* Otherwise, let `scope` be the scope of the innermost block such that `scope`
|
||||
already has a variable named `name`. Set `scope`'s variable `name` to `value`.
|
||||
|
||||
[scope]: ../spec/variables.md#scope
|
||||
[scope]: ../spec/spec.md#scope
|
||||
|
||||
* When a top-level mixin or function declaration `declaration` is encountered:
|
||||
|
||||
|
@ -125,7 +125,7 @@ To execute a `VariableDeclaration` `declaration`:
|
||||
variable named `name`, set the innermost block's scope's variable `name` to
|
||||
`value`.~~
|
||||
|
||||
[scope]: ../spec/variables.md#scope
|
||||
[scope]: ../spec/spec.md#scope
|
||||
|
||||
* **Otherwise, if `resolved` is null, get the innermost block containing
|
||||
`declaration` and set its scope's variable `name` to `value`.**
|
||||
|
23
js-api-doc/compile.d.ts
vendored
23
js-api-doc/compile.d.ts
vendored
@ -44,6 +44,11 @@ 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.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
@ -66,9 +71,9 @@ export function compile(path: string, options?: Options<'sync'>): CompileResult;
|
||||
* This only allows synchronous or asynchronous {@link Importer}s and
|
||||
* {@link CustomFunction}s.
|
||||
*
|
||||
* **Heads up!** When using Dart Sass, **{@link compile} is almost twice as fast
|
||||
* as {@link compileAsync}**, due to the overhead of making the entire
|
||||
* evaluation process asynchronous.
|
||||
* **Heads up!** When using the `sass` npm package, **{@link compile} is almost
|
||||
* twice as fast as {@link compileAsync}**, due to the overhead of making the
|
||||
* entire evaluation process asynchronous.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@ -94,6 +99,12 @@ 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
|
||||
* {@link compileString}**, due to the overhead of emulating synchronous
|
||||
* messaging with worker threads and concurrent compilations being blocked on
|
||||
* main thread.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
@ -125,9 +136,9 @@ export function compileString(
|
||||
* This only allows synchronous or asynchronous {@link Importer}s and {@link
|
||||
* CustomFunction}s.
|
||||
*
|
||||
* **Heads up!** When using Dart Sass, **{@link compile} is almost twice as fast
|
||||
* as {@link compileAsync}**, due to the overhead of making the entire
|
||||
* evaluation process asynchronous.
|
||||
* **Heads up!** When using the `sass` npm package, **{@link compileString} is
|
||||
* almost twice as fast as {@link compileStringAsync}**, due to the overhead
|
||||
* of making the entire evaluation process asynchronous.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
|
11
js-api-doc/legacy/render.d.ts
vendored
11
js-api-doc/legacy/render.d.ts
vendored
@ -96,6 +96,11 @@ export interface LegacyResult {
|
||||
* This function synchronously compiles a Sass file to CSS. If it succeeds, it
|
||||
* returns the result, and if it fails it throws an error.
|
||||
*
|
||||
* **Heads up!** When using the `sass-embedded` npm package, **{@link render}
|
||||
* is almost always faster than {@link renderSync}**, due to the overhead of
|
||||
* emulating synchronous messaging with worker threads and concurrent
|
||||
* compilations being blocked on main thread.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
@ -116,9 +121,9 @@ export function renderSync(options: LegacyOptions<'sync'>): LegacyResult;
|
||||
* `callback` with a {@link LegacyResult} if compilation succeeds or {@link
|
||||
* LegacyException} if it fails.
|
||||
*
|
||||
* **Heads up!** When using Dart Sass, **{@link renderSync} is almost twice as
|
||||
* fast as {@link render}** by default, due to the overhead of making the entire
|
||||
* evaluation process asynchronous.
|
||||
* **Heads up!** When using the `sass` npm package, **{@link renderSync} is
|
||||
* almost twice as fast as {@link render}** by default, due to the overhead of
|
||||
* making the entire evaluation process asynchronous.
|
||||
*
|
||||
* ```js
|
||||
* const sass = require('sass'); // or require('node-sass');
|
||||
|
@ -1,3 +1,54 @@
|
||||
## Draft 1.6
|
||||
|
||||
* Simplify the type definition for `interpolate`, and make `options` argument
|
||||
optional.
|
||||
|
||||
* Fix typo in `changedValue` definition of `color.change`.
|
||||
|
||||
## Draft 1.5
|
||||
|
||||
* Clarify that deprecated SassColor getters (e.g. `red`, `blue`, etc.) convert
|
||||
color to a legacy space before returning channel value.
|
||||
|
||||
## Draft 1.4
|
||||
|
||||
* In `change`, adjust algorithm for differentiating `hwb` from `hsl` when only
|
||||
`hue` and no `space` is specified.
|
||||
|
||||
* In `change` for legacy colors, emit a `color-4-api` warning if a non-alpha
|
||||
channel is explicitly null and no space is set.
|
||||
|
||||
* In procedure for Changing a Component Value, specify that `undefined` values
|
||||
should return the `initialValue`.
|
||||
|
||||
* `toSpace` uses `Converting a Color` algorithm instead of `color.to-space()` to
|
||||
avoid removing missing channels when converting to a legacy space.
|
||||
|
||||
* In `change` and constructors, throw an error for alpha and lightness values
|
||||
that are out of range.
|
||||
|
||||
## Draft 1.3
|
||||
|
||||
* Rename new Embedded Protocol message from `SassColor` to `Color`.
|
||||
|
||||
* Make `color2` a positional parameter of `interpolate`, not an option.
|
||||
|
||||
* Add `rec2020` color space.
|
||||
|
||||
## Draft 1.2
|
||||
|
||||
* Add "alpha" to all channel name types.
|
||||
|
||||
* Remove `isAlphaMissing` in favor of `isChannelMissing("alpha")`.
|
||||
|
||||
* Rename types using title-case for acronyms longer than two letters in
|
||||
camel-case identifiers (e.g. `ColorSpaceHsl` instead of `ColorSpaceHSL`).
|
||||
|
||||
* Remove generic `change` overload, and make `space` optional on others.
|
||||
|
||||
* Return `immutable` types for `channels` and `channelsOrNull`, and remove
|
||||
assumption of 3 channels.
|
||||
|
||||
## Draft 1.1
|
||||
|
||||
* Clarify values in `channels` and `channelsOrNull`.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.1
|
||||
# CSS Color Level 4, New Color Spaces JavaScript API: Draft 1.6
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2831),
|
||||
[Changelog](color-4-new-spaces-js.changes.md))*
|
||||
@ -24,7 +24,6 @@ proposal].
|
||||
* [`channel`](#channel)
|
||||
* [`alpha`](#alpha)
|
||||
* [`isChannelMissing`](#ischannelmissing)
|
||||
* [`isAlphaMissing`](#isalphamissing)
|
||||
* [`isChannelPowerless`](#ischannelpowerless)
|
||||
* [`interpolate`](#interpolate)
|
||||
* [Updated Color Functions](#updated-color-functions)
|
||||
@ -41,15 +40,18 @@ proposal].
|
||||
* [Deprecations](#deprecations)
|
||||
* [Procedures](#procedures)
|
||||
* [Parsing a Channel Value](#parsing-a-channel-value)
|
||||
* [Parsing a Clamped Channel Value](#parsing-a-clamped-channel-value)
|
||||
* [Changing a Component Value](#changing-a-component-value)
|
||||
* [Determining Construction Space](#determining-construction-space)
|
||||
* [Embedded Protocol](#embedded-protocol)
|
||||
* [SassColor](#sasscolor)
|
||||
* [Color](#color)
|
||||
* [Removed SassScript values](#removed-sassscript-values)
|
||||
|
||||
## API
|
||||
|
||||
```ts
|
||||
import {List} from 'immutable';
|
||||
|
||||
import {Value} from '../spec/js-api/value';
|
||||
```
|
||||
|
||||
@ -58,53 +60,54 @@ import {Value} from '../spec/js-api/value';
|
||||
### Color Space Definitions
|
||||
|
||||
```ts
|
||||
export type ColorSpaceHSL = 'hsl';
|
||||
export type ColorSpaceHsl = 'hsl';
|
||||
|
||||
export type ChannelNameHSL = 'hue' | 'saturation' | 'lightness';
|
||||
export type ChannelNameHsl = 'hue' | 'saturation' | 'lightness' | 'alpha';
|
||||
|
||||
export type ColorSpaceHWB = 'hwb';
|
||||
export type ColorSpaceHwb = 'hwb';
|
||||
|
||||
export type ChannelNameHWB = 'hue' | 'whiteness' | 'blackness';
|
||||
export type ChannelNameHwb = 'hue' | 'whiteness' | 'blackness' | 'alpha';
|
||||
|
||||
export type ColorSpaceLab = 'lab' | 'oklab';
|
||||
|
||||
export type ChannelNameLab = 'lightness' | 'a' | 'b';
|
||||
export type ChannelNameLab = 'lightness' | 'a' | 'b' | 'alpha';
|
||||
|
||||
export type ColorSpaceLCH = 'lch' | 'oklch';
|
||||
export type ColorSpaceLch = 'lch' | 'oklch';
|
||||
|
||||
export type ChannelNameLCH = 'lightness' | 'chroma' | 'hue';
|
||||
export type ChannelNameLch = 'lightness' | 'chroma' | 'hue' | 'alpha';
|
||||
|
||||
export type ColorSpaceRGB =
|
||||
export type ColorSpaceRgb =
|
||||
| 'a98-rgb'
|
||||
| 'display-p3'
|
||||
| 'prophoto-rgb'
|
||||
| 'rec2020'
|
||||
| 'rgb'
|
||||
| 'srgb'
|
||||
| 'srgb-linear';
|
||||
|
||||
export type ChannelNameRGB = 'red' | 'green' | 'blue';
|
||||
export type ChannelNameRgb = 'red' | 'green' | 'blue' | 'alpha';
|
||||
|
||||
export type ColorSpaceXYZ = 'xyz' | 'xyz-d50' | 'xyz-d65';
|
||||
export type ColorSpaceXyz = 'xyz' | 'xyz-d50' | 'xyz-d65';
|
||||
|
||||
export type ChannelNameXYZ = 'x' | 'y' | 'z';
|
||||
export type ChannelNameXyz = 'x' | 'y' | 'z' | 'alpha';
|
||||
|
||||
export type ChannelName =
|
||||
| ChannelNameHSL
|
||||
| ChannelNameHWB
|
||||
| ChannelNameHsl
|
||||
| ChannelNameHwb
|
||||
| ChannelNameLab
|
||||
| ChannelNameLCH
|
||||
| ChannelNameRGB
|
||||
| ChannelNameXYZ;
|
||||
| ChannelNameLch
|
||||
| ChannelNameRgb
|
||||
| ChannelNameXyz;
|
||||
|
||||
export type KnownColorSpace =
|
||||
| ColorSpaceHSL
|
||||
| ColorSpaceHWB
|
||||
| ColorSpaceHsl
|
||||
| ColorSpaceHwb
|
||||
| ColorSpaceLab
|
||||
| ColorSpaceLCH
|
||||
| ColorSpaceRGB
|
||||
| ColorSpaceXYZ;
|
||||
| ColorSpaceLch
|
||||
| ColorSpaceRgb
|
||||
| ColorSpaceXyz;
|
||||
|
||||
export type PolarColorSpace = ColorSpaceHSL | ColorSpaceHWB | ColorSpaceLCH;
|
||||
export type PolarColorSpace = ColorSpaceHsl | ColorSpaceHwb | ColorSpaceLch;
|
||||
|
||||
export type RectangularColorSpace = Exclude<KnownColorSpace, PolarColorSpace>;
|
||||
|
||||
@ -135,13 +138,14 @@ get space(): KnownColorSpace;
|
||||
|
||||
* If `this.space` is equal to `space`, return `this`.
|
||||
|
||||
* Otherwise, return the result of [`color.to-space(internal, space)`].
|
||||
* Otherwise, return the result of [Converting a Color] with `this` as
|
||||
`origin-color` and `space` as `target-space`.
|
||||
|
||||
```ts
|
||||
toSpace(space: KnownColorSpace): SassColor;
|
||||
```
|
||||
|
||||
[`color.to-space(internal, space)`]: ./color-4-new-spaces.md#colorto-space
|
||||
[Converting a Color]: ./color-4-new-spaces.md#converting-a-color
|
||||
|
||||
#### `isLegacy`
|
||||
|
||||
@ -177,14 +181,14 @@ toGamut(space?: KnownColorSpace): SassColor;
|
||||
|
||||
#### `channelsOrNull`
|
||||
|
||||
Returns an array of channel values (excluding alpha) for [`internal`], with
|
||||
Returns a list of channel values (excluding alpha) for [`internal`], with
|
||||
[missing channels][missing components] converted to `null`.
|
||||
|
||||
* Let `space` be the result of [`this.space`].
|
||||
* Let `space` be the value of [`this.space`].
|
||||
|
||||
* Let `components` be the list of channels in `space`.
|
||||
|
||||
* Let `channels` be an empty array.
|
||||
* Let `channels` be an empty list.
|
||||
|
||||
* For each `component` in `components`:
|
||||
|
||||
@ -197,7 +201,7 @@ Returns an array of channel values (excluding alpha) for [`internal`], with
|
||||
* Return `channels`.
|
||||
|
||||
```ts
|
||||
get channelsOrNull(): [number | null, number | null, number | null];
|
||||
get channelsOrNull(): List<number | null>;
|
||||
```
|
||||
|
||||
[missing components]: ./color-4-new-spaces.md#missing-components
|
||||
@ -205,12 +209,12 @@ get channelsOrNull(): [number | null, number | null, number | null];
|
||||
|
||||
#### `channels`
|
||||
|
||||
This algorithm returns an array of channel values (excluding alpha) for
|
||||
This algorithm returns a list of channel values (excluding alpha) for
|
||||
[`internal`], with [missing channels][missing components] converted to `0`.
|
||||
|
||||
* Let `channelsOrNull` be the result of [`this.channelsOrNull`].
|
||||
* Let `channelsOrNull` be the value of [`this.channelsOrNull`].
|
||||
|
||||
* Let `channels` be an empty array.
|
||||
* Let `channels` be an empty list.
|
||||
|
||||
* For each `channel` in `channelsOrNull`:
|
||||
|
||||
@ -223,12 +227,12 @@ This algorithm returns an array of channel values (excluding alpha) for
|
||||
[`this.channelsOrNull`]: #channelsornull
|
||||
|
||||
```ts
|
||||
get channels(): [number, number, number];
|
||||
get channels(): List<number>;
|
||||
```
|
||||
|
||||
#### `channel`
|
||||
|
||||
* Let `initialSpace` be the value of [`this.space()`].
|
||||
* Let `initialSpace` be the value of [`this.space`].
|
||||
|
||||
* Let `space` be `options.space` if it is defined, and the value of
|
||||
`initialSpace` otherwise.
|
||||
@ -239,36 +243,18 @@ get channels(): [number, number, number];
|
||||
|
||||
* Let `value` be the channel value in `color` with name of `component`.
|
||||
|
||||
* If `value` is null, return 0.
|
||||
* If `value` is `null`, return 0.
|
||||
|
||||
* Otherwise, return `value`.
|
||||
|
||||
```ts
|
||||
channel(channel: ChannelName): number;
|
||||
channel(
|
||||
channel: ChannelNameHSL | 'alpha',
|
||||
options: {space: ColorSpaceHSL}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameHWB | 'alpha',
|
||||
options: {space: ColorSpaceHWB}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameLab | 'alpha',
|
||||
options: {space: ColorSpaceLab}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameLCH | 'alpha',
|
||||
options: {space: ColorSpaceLCH}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameRGB | 'alpha',
|
||||
options: {space: ColorSpaceRGB}
|
||||
): number;
|
||||
channel(
|
||||
channel: ChannelNameXYZ | 'alpha',
|
||||
options: {space: ColorSpaceXYZ}
|
||||
): number;
|
||||
channel(channel: ChannelNameHsl, options: {space: ColorSpaceHsl}): number;
|
||||
channel(channel: ChannelNameHwb, options: {space: ColorSpaceHwb}): number;
|
||||
channel(channel: ChannelNameLab, options: {space: ColorSpaceLab}): number;
|
||||
channel(channel: ChannelNameLch, options: {space: ColorSpaceLch}): number;
|
||||
channel(channel: ChannelNameRgb, options: {space: ColorSpaceRgb}): number;
|
||||
channel(channel: ChannelNameXyz, options: {space: ColorSpaceXyz}): number;
|
||||
```
|
||||
|
||||
#### `alpha`
|
||||
@ -287,20 +273,11 @@ Returns the result of [`color.is-missing(internal,
|
||||
channel)`][color.is-missing()] as a JavaScript boolean.
|
||||
|
||||
```ts
|
||||
isChannelMissing(channel: ChannelName | 'alpha'): boolean;
|
||||
isChannelMissing(channel: ChannelName): boolean;
|
||||
```
|
||||
|
||||
[color.is-missing()]: ./color-4-new-spaces.md#coloris-missing-1
|
||||
|
||||
#### `isAlphaMissing`
|
||||
|
||||
Returns the result of [`color.is-missing(internal,
|
||||
'alpha')`][color.is-missing()] as a JavaScript boolean.
|
||||
|
||||
```ts
|
||||
get isAlphaMissing(): boolean;
|
||||
```
|
||||
|
||||
#### `isChannelPowerless`
|
||||
|
||||
Returns the result of [`color.is-powerless(internal, channel, space)`] as a
|
||||
@ -311,34 +288,34 @@ JavaScript boolean.
|
||||
```ts
|
||||
isChannelPowerless(channel: ChannelName): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameHSL,
|
||||
options?: {space: ColorSpaceHSL}
|
||||
channel: ChannelNameHsl,
|
||||
options?: {space: ColorSpaceHsl}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameHWB,
|
||||
options?: {space: ColorSpaceHWB}
|
||||
channel: ChannelNameHwb,
|
||||
options?: {space: ColorSpaceHwb}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameLab,
|
||||
options?: {space: ColorSpaceLab}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameLCH,
|
||||
options?: {space: ColorSpaceLCH}
|
||||
channel: ChannelNameLch,
|
||||
options?: {space: ColorSpaceLch}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameRGB,
|
||||
options?: {space: ColorSpaceRGB}
|
||||
channel: ChannelNameRgb,
|
||||
options?: {space: ColorSpaceRgb}
|
||||
): boolean;
|
||||
isChannelPowerless(
|
||||
channel: ChannelNameXYZ,
|
||||
options?: {space: ColorSpaceXYZ}
|
||||
channel: ChannelNameXyz,
|
||||
options?: {space: ColorSpaceXyz}
|
||||
): boolean;
|
||||
```
|
||||
|
||||
#### `interpolate`
|
||||
|
||||
* Let `space` be the value of [`this.space()`].
|
||||
* Let `space` be the value of [`this.space`].
|
||||
|
||||
* If `options.method` is set, let `interpolationMethod` be a space separated
|
||||
list containing the value of `space`, a space, and the value of
|
||||
@ -350,16 +327,16 @@ isChannelPowerless(
|
||||
* Otherwise, let `interpolationMethod` be a space separated list containing the
|
||||
value of `space`, a space, and the string "shorter".
|
||||
|
||||
* Return the result of [`color.mix(internal, options.color2, options.weight, interpolationMethod)`][`color.mix()`].
|
||||
* Return the result of [`color.mix(internal, color2, options.weight, interpolationMethod)`][`color.mix()`].
|
||||
|
||||
```ts
|
||||
interpolate(options: {color2: SassColor; weight?: number}): SassColor;
|
||||
|
||||
interpolate(options: {
|
||||
color2: SassColor;
|
||||
weight?: number;
|
||||
method?: HueInterpolationMethod;
|
||||
}): SassColor;
|
||||
interpolate(
|
||||
color2: SassColor,
|
||||
options?: {
|
||||
weight?: number;
|
||||
method?: HueInterpolationMethod;
|
||||
}
|
||||
): SassColor;
|
||||
```
|
||||
|
||||
[`color.mix()`]: ./color-4-new-spaces.md#colormix-1
|
||||
@ -381,7 +358,7 @@ as the result of changing some of [`internal`]'s components.
|
||||
> If `space` is not a [legacy color space], a channel value of `null` will
|
||||
> result in a [missing component][missing components] value for that channel.
|
||||
|
||||
* Let `initialSpace` be the value of [`this.space()`].
|
||||
* Let `initialSpace` be the value of [`this.space`].
|
||||
|
||||
* Let `spaceSetExplicitly` be `true` if `options.space` is defined, and `false`
|
||||
otherwise.
|
||||
@ -393,6 +370,9 @@ as the result of changing some of [`internal`]'s components.
|
||||
|
||||
* If `options.whiteness` or `options.blackness` is set, let `space` be `hwb`.
|
||||
|
||||
* Otherwise, if `options.hue` is set and `initialSpace` is `hwb`, let space be
|
||||
`hwb`.
|
||||
|
||||
* Otherwise, if `options.hue`, `options.saturation`, or `options.lightness` is
|
||||
set, let `space` be `hsl`.
|
||||
|
||||
@ -411,16 +391,25 @@ as the result of changing some of [`internal`]'s components.
|
||||
* If any key in `keys` is not the name of a channel in `components`, throw an
|
||||
error.
|
||||
|
||||
* If `options.alpha` is set, and isn't either null or a number between 0 and 1
|
||||
(inclusive and fuzzy), throw an error.
|
||||
|
||||
* If `options.lightness` is set, and isn't either null or a number between 0 and
|
||||
the maximum channel value for the space (inclusive and fuzzy), throw an error.
|
||||
|
||||
* Let `color` be the result of [`this.toSpace(space)`].
|
||||
|
||||
* Let `changedValue` be a function that takes a string argument for `channel`
|
||||
and calls the procedure [`Changing a Component Value`] with `changes` and
|
||||
`this` as `initial`.
|
||||
`color` as `initial`.
|
||||
|
||||
* If `space` equals `hsl` and `spaceSetExplicitly` is `false`:
|
||||
|
||||
* If any of `options.hue`, `options.saturation`, `options.lightness` or
|
||||
`options.alpha` equals null, emit a deprecation warning named `null-alpha`.
|
||||
* If any of `options.hue`, `options.saturation` or `options.lightness` equals
|
||||
`null`, emit a deprecation warning named `color-4-api`.
|
||||
|
||||
* If `options.alpha` equals `null`, emit a deprecation warning named
|
||||
`null-alpha`.
|
||||
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
@ -449,8 +438,11 @@ as the result of changing some of [`internal`]'s components.
|
||||
|
||||
* If `space` equals `hwb` and `spaceSetExplicitly` is `false`:
|
||||
|
||||
* If any of `options.hue`, `options.whiteness`, `options.blackness` or
|
||||
`options.alpha` equals null, emit a deprecation warning named `null-alpha`.
|
||||
* If any of `options.hue`, `options.whiteness` or `options.blackness` equals
|
||||
`null`, emit a deprecation warning named `color-4-api`.
|
||||
|
||||
* If `options.alpha` equals `null`, emit a deprecation warning named
|
||||
`null-alpha`.
|
||||
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
@ -479,8 +471,11 @@ as the result of changing some of [`internal`]'s components.
|
||||
|
||||
* If `space` equals `rgb` and `spaceSetExplicitly` is `false`:
|
||||
|
||||
* If any of `options.red`, `options.green`, `options.blue` or `options.alpha`
|
||||
equals null, emit a deprecation warning named `null-alpha`.
|
||||
* If any of `options.red`, `options.green` or `options.blue` equals
|
||||
`null`, emit a deprecation warning named `color-4-api`.
|
||||
|
||||
* If `options.alpha` equals `null`, emit a deprecation warning named
|
||||
`null-alpha`.
|
||||
|
||||
* Let `changedColor` be the result of:
|
||||
|
||||
@ -531,8 +526,8 @@ as the result of changing some of [`internal`]'s components.
|
||||
})
|
||||
```
|
||||
|
||||
* If `space` equals `a98-rgb`, `display-p3`, `prophoto-rgb`, `srgb`, or
|
||||
`srgb-linear`, let `changedColor` be the result of:
|
||||
* If `space` equals `a98-rgb`, `display-p3`, `prophoto-rgb`, `rec2020`, `srgb`,
|
||||
or `srgb-linear`, let `changedColor` be the result of:
|
||||
|
||||
```js
|
||||
new SassColor({
|
||||
@ -559,7 +554,6 @@ as the result of changing some of [`internal`]'s components.
|
||||
|
||||
* Return the result of [`changedColor.toSpace(initialSpace)`].
|
||||
|
||||
[`this.space()`]: #space
|
||||
[`this.toSpace(space)`]: #tospace
|
||||
[`changedColor.toSpace(initialSpace)`]: #tospace
|
||||
[`Changing a Component Value`]: #changing-a-component-value
|
||||
@ -567,25 +561,17 @@ as the result of changing some of [`internal`]'s components.
|
||||
```ts
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelName]?: number | null;
|
||||
} & {alpha?: number}
|
||||
): SassColor;
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameHSL]?: number | null;
|
||||
[key in ChannelNameHsl]?: number | null;
|
||||
} & {
|
||||
alpha?: number;
|
||||
space: ColorSpaceHSL;
|
||||
space?: ColorSpaceHsl;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameHWB]?: number | null;
|
||||
[key in ChannelNameHwb]?: number | null;
|
||||
} & {
|
||||
alpha?: number;
|
||||
space: ColorSpaceHWB;
|
||||
space?: ColorSpaceHwb;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
@ -593,35 +579,31 @@ change(
|
||||
options: {
|
||||
[key in ChannelNameLab]?: number | null;
|
||||
} & {
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceLab;
|
||||
space?: ColorSpaceLab;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameLCH]?: number | null;
|
||||
[key in ChannelNameLch]?: number | null;
|
||||
} & {
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceLCH;
|
||||
space?: ColorSpaceLch;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameRGB]?: number | null;
|
||||
[key in ChannelNameRgb]?: number | null;
|
||||
} & {
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceRGB;
|
||||
space?: ColorSpaceRgb;
|
||||
}
|
||||
): SassColor;
|
||||
|
||||
change(
|
||||
options: {
|
||||
[key in ChannelNameXYZ]?: number | null;
|
||||
[key in ChannelNameXyz]?: number | null;
|
||||
} & {
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceXYZ;
|
||||
space?: ColorSpaceXyz;
|
||||
}
|
||||
): SassColor;
|
||||
```
|
||||
@ -637,17 +619,21 @@ change(
|
||||
|
||||
#### Lab Channel Constructor
|
||||
|
||||
Create a new SassColor in a color space with Lab channels -- `lab` and `oklab`.
|
||||
Create a new SassColor in a color space with Lab channels—`lab` and `oklab`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a channel value] with value
|
||||
`options.lightness`.
|
||||
* If `options.space` equals `lab`, let `maximum` be `100`. Otherwise, let
|
||||
`maximum` be `1`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a clamped channel value] with
|
||||
`value` of `options.lightness`, `minimum` of `0`, and `maximum` of `maximum`.
|
||||
|
||||
* Let `a` be the result of [parsing a channel value] with value `options.a`.
|
||||
|
||||
* Let `b` be the result of [parsing a channel value] with value `options.b`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with value `options.alpha`,
|
||||
`minimum` of 0, and `maximum` of 1.
|
||||
|
||||
* If `options.space` equals `lab`, set [`internal`] to the result of
|
||||
[`lab(lightness a b / alpha)`].
|
||||
@ -658,6 +644,7 @@ Create a new SassColor in a color space with Lab channels -- `lab` and `oklab`.
|
||||
[`lab(lightness a b / alpha)`]: ./color-4-new-spaces.md#lab
|
||||
[`oklab(lightness a b / alpha)`]: ./color-4-new-spaces.md#oklab
|
||||
[parsing a channel value]: #parsing-a-channel-value
|
||||
[parsing a clamped channel value]: #parsing-a-clamped-channel-value
|
||||
|
||||
```ts
|
||||
constructor(options: {
|
||||
@ -671,17 +658,21 @@ constructor(options: {
|
||||
|
||||
#### LCH Channel Constructor
|
||||
|
||||
Create a new SassColor in a color space with LCH channels -- `lch` and `oklch`.
|
||||
Create a new SassColor in a color space with LCH channels—`lch` and `oklch`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a channel value] with value
|
||||
`options.lightness`.
|
||||
* If `options.space` equals `lch`, let `maximum` be `100`. Otherwise, let
|
||||
`maximum` be `1`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a clamped channel value] with
|
||||
`value` of `options.lightness`, `minimum` of `0`, and `maximum` of `maximum`.
|
||||
|
||||
* Let `c` be the result of [parsing a channel value] with value `options.c`.
|
||||
|
||||
* Let `h` be the result of [parsing a channel value] with value `options.h`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with value `options.alpha`,
|
||||
`minimum` of 0, and `maximum` of 1.
|
||||
|
||||
* If `options.space` equals `lch`, set [`internal`] to the result of
|
||||
[`lch(lightness a b / alpha)`].
|
||||
@ -698,14 +689,14 @@ constructor(options: {
|
||||
chroma: number | null;
|
||||
hue: number | null;
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceLCH;
|
||||
space: ColorSpaceLch;
|
||||
});
|
||||
```
|
||||
|
||||
#### Predefined RGB Channel Constructor
|
||||
|
||||
Create a new SassColor in a color space with RGB channels -- `srgb`,
|
||||
`srgb-linear`, `display-p3`, `a98-rgb`, and `prophoto-rgb`. `rgb` is supported
|
||||
Create a new SassColor in a color space with RGB channels—`srgb`, `srgb-linear`,
|
||||
`display-p3`, `a98-rgb`, `prophoto-rgb`, and `rec2020`. `rgb` is supported
|
||||
through the modified [RGB Constructor].
|
||||
|
||||
* Let `red` be the result of [parsing a channel value] with value `options.red`.
|
||||
@ -717,7 +708,8 @@ through the modified [RGB Constructor].
|
||||
`options.blue`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with value `options.alpha`,
|
||||
`minimum` of 0, and `maximum` of 1.
|
||||
|
||||
* Let `space` be the unquoted string value of `options.space`.
|
||||
|
||||
@ -732,14 +724,14 @@ constructor(options: {
|
||||
green: number | null;
|
||||
blue: number | null;
|
||||
alpha?: number | null;
|
||||
space: Exclude<ColorSpaceRGB, 'rgb'>;
|
||||
space: Exclude<ColorSpaceRgb, 'rgb'>;
|
||||
});
|
||||
```
|
||||
|
||||
#### XYZ Channel Constructor
|
||||
|
||||
Create a new SassColor in a color space with XYZ channels -- `xyz`, `xyz-d50`,
|
||||
and `xyz-d65`.
|
||||
Create a new SassColor in a color space with XYZ channels—`xyz`, `xyz-d50`, and
|
||||
`xyz-d65`.
|
||||
|
||||
* Let `x` be the result of [parsing a channel value] with value `options.x`.
|
||||
|
||||
@ -748,7 +740,8 @@ and `xyz-d65`.
|
||||
* Let `z` be the result of [parsing a channel value] with value `options.z`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with value `options.alpha`,
|
||||
`minimum` of 0, and `maximum` of 1.
|
||||
|
||||
* Let `space` be the unquoted string value of `options.space`.
|
||||
|
||||
@ -762,7 +755,7 @@ constructor(options: {
|
||||
y: number | null;
|
||||
z: number | null;
|
||||
alpha?: number | null;
|
||||
space: ColorSpaceXYZ;
|
||||
space: ColorSpaceXyz;
|
||||
});
|
||||
```
|
||||
|
||||
@ -784,11 +777,12 @@ Create a new SassColor in the `hsl` color space.
|
||||
* Let `saturation` be the result of [parsing a channel value] with value
|
||||
`options.saturation`.
|
||||
|
||||
* Let `lightness` be the result of [parsing a channel value] with value
|
||||
`options.lightness`.
|
||||
* Let `lightness` be the result of [parsing a clamped channel value] with
|
||||
`value` of `options.lightness`, `minimum` of `0`, and `maximum` of `100`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with `value` of
|
||||
`options.alpha`, `minimum` of `0`, and `maximum` of `1`.
|
||||
|
||||
* Set [`internal`] to the result of [`hsl(hue saturation lightness / alpha)`].
|
||||
|
||||
@ -800,7 +794,7 @@ constructor(options: {
|
||||
saturation: number | null;
|
||||
lightness: number | null;
|
||||
alpha?: number | null;
|
||||
space?: ColorSpaceHSL;
|
||||
space?: ColorSpaceHsl;
|
||||
});
|
||||
```
|
||||
|
||||
@ -820,7 +814,8 @@ Create a new SassColor in the `hwb` color space.
|
||||
`options.blackness`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with `value` of
|
||||
`options.alpha`, `minimum` of `0`, and `maximum` of `1`.
|
||||
|
||||
* Set [`internal`] to the result of [`hwb(hue whiteness blackness / alpha)`].
|
||||
|
||||
@ -832,7 +827,7 @@ constructor(options: {
|
||||
whiteness: number | null;
|
||||
blackness: number | null;
|
||||
alpha?: number | null;
|
||||
space?: ColorSpaceHWB;
|
||||
space?: ColorSpaceHwb;
|
||||
});
|
||||
```
|
||||
|
||||
@ -852,9 +847,10 @@ Create a new SassColor in the `rgb` color space.
|
||||
`options.blue`.
|
||||
|
||||
* If `options.alpha` is not set, let `alpha` be `1`. Otherwise, let `alpha` be
|
||||
the result of [parsing a channel value] with value `options.alpha`.
|
||||
the result of [parsing a clamped channel value] with `value` of
|
||||
`options.alpha`, `minimum` of `0`, and `maximum` of `1`.
|
||||
|
||||
* Return the result of [`rgb(red green blue / alpha)`].
|
||||
* Set [`internal`] to the result of [`rgb(red green blue / alpha)`].
|
||||
|
||||
[`rgb(red green blue / alpha)`]: ./color-4-new-spaces.md#rgb-and-rgba
|
||||
|
||||
@ -878,17 +874,31 @@ A number of SassColor getters only make sense for [legacy color space], and so
|
||||
are being deprecated in favor of the new [`channel`] function. This deprecation
|
||||
is called `color-4-api`.
|
||||
|
||||
[`channel`]: #channel
|
||||
The following deprecated getters return the result of
|
||||
[`this.channel(channelName, { space: "rgb" })`][`channel`] where `channelName`
|
||||
is the name of the respective getter.
|
||||
|
||||
* `red`
|
||||
* `green`
|
||||
* `blue`
|
||||
|
||||
The following deprecated getters return the result of
|
||||
[`this.channel(channelName, { space: "hsl" })`][`channel`] where `channelName`
|
||||
is the name of the respective getter.
|
||||
|
||||
* `hue`
|
||||
* `saturation`
|
||||
* `lightness`
|
||||
|
||||
The following deprecated getters return the result of
|
||||
[`this.channel(channelName, { space: "hwb" })`][`channel`] where `channelName`
|
||||
is the name of the respective getter.
|
||||
|
||||
* `whiteness`
|
||||
* `blackness`
|
||||
|
||||
[`channel`]: #channel
|
||||
|
||||
## Procedures
|
||||
|
||||
### Parsing a Channel Value
|
||||
@ -901,6 +911,18 @@ This procedure takes a channel value `value`, and returns the special value
|
||||
* If `value` is the Javascript value `null`, return the unquoted Sass string
|
||||
`none`.
|
||||
|
||||
### Parsing a Clamped Channel Value
|
||||
|
||||
This procedure takes a channel value `value` and an inclusive range of `minimum`
|
||||
and `maximum`. It asserts the value is in the range, and returns the special
|
||||
value `none` if the value is `null`.
|
||||
|
||||
* If `value` is fuzzy less-than `minimum`, throw an error.
|
||||
|
||||
* If `value` is fuzzy greater-than `maximum`, throw an error.
|
||||
|
||||
* Otherwise, return the result of [Parsing a Channel Value].
|
||||
|
||||
### Changing a Component Value
|
||||
|
||||
This procedure takes a `channel` name, an object `changes` and a SassColor
|
||||
@ -911,7 +933,11 @@ This procedure takes a `channel` name, an object `changes` and a SassColor
|
||||
|
||||
* If `channel` is not a key in `changes`, return `initialValue`.
|
||||
|
||||
* Otherwise, return the value for `channel` in `changes`.
|
||||
* Let `changedValue` be the value for `channel` in `changes`.
|
||||
|
||||
* If `changedValue` is `undefined` and not `null`, return `initialValue`.
|
||||
|
||||
* Otherwise, return `changedValue`.
|
||||
|
||||
### Determining Construction Space
|
||||
|
||||
@ -933,10 +959,10 @@ space for construction.
|
||||
This introduces a breaking change in the Embedded Protocol, as it removes the
|
||||
legacy SassScript values.
|
||||
|
||||
### SassColor
|
||||
### Color
|
||||
|
||||
```proto
|
||||
message SassColor {
|
||||
message Color {
|
||||
// The name of a known color space.
|
||||
string space = 1;
|
||||
|
||||
@ -949,8 +975,7 @@ message SassColor {
|
||||
// The value of the third channel associated with `space`.
|
||||
double channel3 = 4;
|
||||
|
||||
// The color's alpha channel. Mandatory. Must be between 0 and 1,
|
||||
// inclusive.
|
||||
// The color's alpha channel. Mandatory. Must be between 0 and 1, inclusive.
|
||||
double alpha = 5;
|
||||
}
|
||||
```
|
||||
|
@ -1,3 +1,12 @@
|
||||
## 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Package Importer
|
||||
# Package Importer: Draft 1.3
|
||||
|
||||
*([Issue](https://github.com/sass/sass/issues/2739))*
|
||||
|
||||
@ -225,8 +225,8 @@ import {FileImporter, Importer} from '../spec/js-api/importer';
|
||||
### `nodePackageImporter`
|
||||
|
||||
```ts
|
||||
type NodePackageImporter = {
|
||||
_NodePackageImporterBrand: any;
|
||||
export type NodePackageImporter = {
|
||||
_NodePackageImporterBrand: unknown;
|
||||
};
|
||||
export declare const nodePackageImporter: NodePackageImporter;
|
||||
```
|
||||
@ -440,7 +440,9 @@ This algorithm takes a package.json value `packageManifest`, a directory URL
|
||||
|
||||
* If `exports` is undefined, return null.
|
||||
|
||||
* Let `subpathVariants` be the result of [Export load paths] with `subpath`.
|
||||
* If `subpath` is empty, let `subpathVariants` be an array with the string `.`.
|
||||
Otherwise, 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",
|
||||
|
12
proposal/shared-resources.changes.md
Normal file
12
proposal/shared-resources.changes.md
Normal file
@ -0,0 +1,12 @@
|
||||
## 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
|
226
proposal/shared-resources.d.ts.md
Normal file
226
proposal/shared-resources.d.ts.md
Normal file
@ -0,0 +1,226 @@
|
||||
# Shared Resources in JavaScript API: Draft 1.1
|
||||
|
||||
*([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 interface Compiler {
|
||||
```
|
||||
|
||||
##### `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 interface AsyncCompiler {
|
||||
```
|
||||
|
||||
##### `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>;
|
||||
}
|
||||
```
|
@ -53,15 +53,16 @@ To execute a `@for` rule `rule`:
|
||||
|
||||
* While `i` is not equal to `to`:
|
||||
|
||||
* Let `scope` be a new [scope].
|
||||
* [In a new scope]:
|
||||
|
||||
* Add a variable with `rule`'s `VariableName` as its name and `i` as its value
|
||||
to `scope`.
|
||||
* Add a variable with `rule`'s `VariableName` as its name and `i` as its value
|
||||
to the [current scope].
|
||||
|
||||
> Note that this variable will have the same unit that `from`.
|
||||
|
||||
* Execute the `ForBlock`'s statements in `scope`.
|
||||
|
||||
* Set `i` to `i + direction`.
|
||||
> Note that this variable will have the same unit that `from`.
|
||||
|
||||
[scope]: ../variables.md#scope
|
||||
* Execute the `ForBlock`'s statements.
|
||||
|
||||
* Set `i` to `i + direction`.
|
||||
|
||||
[In a new scope]: ../spec.md#running-in-a-new-scope
|
||||
[current scope]: ../spec.md#scope
|
||||
|
@ -28,20 +28,38 @@ To execute a `@function` rule `rule`:
|
||||
|
||||
[vendor prefix]: ../syntax.md#vendor-prefix
|
||||
|
||||
* Let `parent` be the [current scope].
|
||||
|
||||
[current scope]: ../spec.md#scope
|
||||
|
||||
* Let `function` be a [function] named `name` which does the following when
|
||||
executed with `args`:
|
||||
|
||||
[function]: ../types/functions.md
|
||||
|
||||
* With the current scope set to an empty [scope] with `parent` as its parent:
|
||||
|
||||
* Evaluate `args` with `rule`'s `ArgumentDeclaration`.
|
||||
|
||||
* Execute each statement in `rule`.
|
||||
|
||||
* Return the value from the `@return` rule if one was executed, or throw an
|
||||
error if no `@return` rule was executed.
|
||||
|
||||
[scope]: ../spec.md#scope
|
||||
|
||||
* If `rule` is outside of any block of statements:
|
||||
|
||||
* If `name` *doesn't* begin with `-` or `_`, set [the current module][]'s
|
||||
function `name` to `rule`.
|
||||
function `name` to `function`.
|
||||
|
||||
> This overrides the previous definition, if one exists.
|
||||
|
||||
* Set [the current import context][]'s function `name` to `rule`.
|
||||
* Set [the current import context]'s function `name` to `function`.
|
||||
|
||||
> This happens regardless of whether or not it begins with `-` or `_`.
|
||||
|
||||
[the current module]: ../spec.md#current-module
|
||||
[the current import context]: ../spec.md#current-import-context
|
||||
|
||||
* Otherwise, set the innermost block's [scope][]'s function `name` to `value`.
|
||||
|
||||
[scope]: ../variables.md#scope
|
||||
* Otherwise, set the [current scope]'s function `name` to `function`.
|
||||
|
@ -138,7 +138,7 @@ To execute an `@import` rule `rule`:
|
||||
* Add `imported`'s [extensions][] to the current module.
|
||||
|
||||
* If the `@import` rule is nested within at-rules and/or style rules, add each
|
||||
member in `imported` to the local [scope][].
|
||||
member in `imported` to the [current scope].
|
||||
|
||||
* Otherwise, add each member in `imported` to the current import context and
|
||||
the current module.
|
||||
@ -158,4 +158,4 @@ To execute an `@import` rule `rule`:
|
||||
[current import context]: ../spec.md#current-import-context
|
||||
[resolving `imported`'s extensions]: extend.md#resolving-a-modules-extensions
|
||||
[extensions]: extend.md#extension
|
||||
[scope]: ../variables.md#scope
|
||||
[current scope]: ../spec.md#scope
|
||||
|
@ -31,23 +31,41 @@ To execute a `@mixin` rule `rule`:
|
||||
|
||||
* Let `name` be the value of `rule`'s `Identifier`.
|
||||
|
||||
* Let `parent` be the [current scope].
|
||||
|
||||
[current scope]: ../spec.md#scope
|
||||
|
||||
* Let `mixin` be a [mixin] named `name` which accepts a content block if `rule`
|
||||
contains a `@content` rule. To execute this mixin with `args`:
|
||||
|
||||
[mixin]: ../types/mixins.md
|
||||
|
||||
* With the current scope set to an empty [scope] with `parent` as its parent:
|
||||
|
||||
* Evaluate `args` with `rule`'s `ArgumentDeclaration`.
|
||||
|
||||
* Execute each statement in `rule`.
|
||||
|
||||
[scope]: ../spec.md#scope
|
||||
|
||||
* If `rule` is outside of any block of statements:
|
||||
|
||||
* If `name` *doesn't* begin with `-` or `_`, set [the current module][]'s
|
||||
mixin `name` to `rule`.
|
||||
* If `name` *doesn't* begin with `-` or `_`, set [the current module]'s
|
||||
mixin `name` to `mixin`.
|
||||
|
||||
> This overrides the previous definition, if one exists.
|
||||
|
||||
* Set [the current import context][]'s mixin `name` to `rule`.
|
||||
* Set [the current import context]'s mixin `name` to `mixin`.
|
||||
|
||||
> This happens regardless of whether or not it begins with `-` or `_`.
|
||||
|
||||
* Otherwise, set the innermost block's [scope][]'s mixin `name` to `value`.
|
||||
|
||||
[scope]: ../variables.md#scope
|
||||
[the current module]: ../spec.md#current-module
|
||||
[the current import context]: ../spec.md#current-import-context
|
||||
|
||||
* Otherwise, set the [current scope]'s mixin `name` to `mixin`.
|
||||
|
||||
> This overrides the previous definition, if one exists.
|
||||
|
||||
## `@include`
|
||||
|
||||
### Syntax
|
||||
@ -70,15 +88,12 @@ To execute an `@include` rule `rule`:
|
||||
|
||||
* Let `name` be `rule`'s `NamespacedIdentifier`.
|
||||
|
||||
* Let `mixin` be the result of [resolving a mixin][] named `name`. If this
|
||||
returns null, throw an error.
|
||||
* Let `mixin` be the result of [resolving a mixin] named `name`. If this returns
|
||||
null, throw an error.
|
||||
|
||||
[resolving a mixin]: ../modules.md#resolving-a-member
|
||||
|
||||
* Execute `rule`'s `ArgumentInvocation` with `mixin`'s `ArgumentDeclaration` in
|
||||
`mixin`'s scope.
|
||||
|
||||
* Execute each statement in `mixin`.
|
||||
* Execute `mixin` with `rule`'s `ArgumentInvocation`.
|
||||
|
||||
## `@content`
|
||||
|
||||
|
38
spec/built-in-modules.md
Normal file
38
spec/built-in-modules.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Built-In Modules
|
||||
|
||||
Sass provides a number of built-in [modules] that may be loaded from URLs with
|
||||
the scheme `sass`. These modules have no extensions, CSS trees, dependencies, or
|
||||
source files. Their canonical URLs are the same as the URLs used to load them.
|
||||
|
||||
[modules]: modules.md#module
|
||||
|
||||
## Built-In Functions and Mixins
|
||||
|
||||
Each function and mixin defined in a built-in modules is specified with a
|
||||
signature of the form
|
||||
|
||||
<x><pre>
|
||||
[\<ident-token>] ArgumentDeclaration
|
||||
</pre></x>
|
||||
|
||||
[\<ident-token>]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
|
||||
|
||||
followed by a procedure. It's available as a member (either function or mixin)
|
||||
in the module whose name is the value of the `<ident-token>`. When it's executed
|
||||
with `args`:
|
||||
|
||||
* With an empty scope with no parent as the [current scope]:
|
||||
|
||||
[current scope]: spec.md#scope
|
||||
|
||||
* Evaluate `args` with the signature's `ArgumentDeclaration`.
|
||||
|
||||
* Run the procedure, and return its result if this is a function.
|
||||
|
||||
Built-in mixins don't accept content blocks unless explicitly specified
|
||||
otherwise.
|
||||
|
||||
By convention, in these procedures `$var` is used as a shorthand for "the value
|
||||
of the variable `var` in the current scope".
|
||||
|
||||
> In other words, `$var` is the value passed to the argument `$var`.
|
@ -5,6 +5,7 @@ This built-in module is available from the URL `sass:meta`.
|
||||
## Table of Contents
|
||||
|
||||
* [Functions](#functions)
|
||||
* [`accepts-content()`](#accepts-content)
|
||||
* [`calc-name()`](#calc-name)
|
||||
* [`calc-args()`](#calc-args)
|
||||
* [`call()`](#call)
|
||||
@ -12,19 +13,36 @@ This built-in module is available from the URL `sass:meta`.
|
||||
* [`feature-exists()`](#feature-exists)
|
||||
* [`function-exists()`](#function-exists)
|
||||
* [`get-function()`](#get-function)
|
||||
* [`get-mixin()`](#get-mixin)
|
||||
* [`global-variable-exists()`](#global-variable-exists)
|
||||
* [`inspect()`](#inspect)
|
||||
* [`keywords()`](#keywords)
|
||||
* [`mixin-exists()`](#mixin-exists)
|
||||
* [`module-functions()`](#module-functions)
|
||||
* [`module-mixins()`](#module-mixins)
|
||||
* [`module-variables()`](#module-variables)
|
||||
* [`type-of()`](#type-of)
|
||||
* [`variable-exists()`](#variable-exists)
|
||||
* [Mixins](#mixins)
|
||||
* [`apply()`](#apply)
|
||||
* [`load-css()`](#load-css)
|
||||
|
||||
## Functions
|
||||
|
||||
### `accepts-content()`
|
||||
|
||||
This is a new function in the `sass:meta` module.
|
||||
|
||||
```
|
||||
accepts-content($mixin)
|
||||
```
|
||||
|
||||
* If `$mixin` is not a [mixin], throw an error.
|
||||
|
||||
[mixin]: ../types/mixins.md
|
||||
|
||||
* Return whether `$mixin` accepts a content block as a SassScript boolean.
|
||||
|
||||
### `calc-name()`
|
||||
|
||||
```
|
||||
@ -90,6 +108,10 @@ This function is also available as a global function named `function-exists()`.
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not an [`<ident-token>`], return false.
|
||||
|
||||
[`<ident-token>`]: https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* Return whether [resolving a function][] named `$name` returns null.
|
||||
@ -117,6 +139,8 @@ This function is also available as a global function named `get-function()`.
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not an [`<ident-token>`], throw an error.
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* If `$css` is falsey:
|
||||
@ -144,6 +168,33 @@ This function is also available as a global function named `get-function()`.
|
||||
* Return [`use`'s module][]'s function named `$name`, or throw an error if no
|
||||
such function exists.
|
||||
|
||||
### `get-mixin()`
|
||||
|
||||
```
|
||||
get-mixin($name, $module: null)
|
||||
```
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not an [`<ident-token>`], throw an error.
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* Return the result of [resolving a mixin] named `$name`. If this returns
|
||||
null, throw an error.
|
||||
|
||||
[resolving a mixin]: ../modules.md#resolving-a-member
|
||||
|
||||
* Otherwise:
|
||||
|
||||
* If `$module` is not a string, throw an error.
|
||||
|
||||
* Let `use` be the `@use` rule in [the current source file] whose namespace is
|
||||
equal to `$module`. If no such rule exists, throw an error.
|
||||
|
||||
* Return [`use`'s module]'s mixin named `$name`, or throw an error if no such
|
||||
mixin exists.
|
||||
|
||||
### `global-variable-exists()`
|
||||
|
||||
```
|
||||
@ -154,6 +205,10 @@ This function is also available as a global function named `global-variable-exis
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not a [`PlainVariable`], return false.
|
||||
|
||||
[`PlainVariable`]: ../variables.md#syntax
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* Return whether [resolving a variable][] named `$name`, ignoring local
|
||||
@ -196,18 +251,18 @@ This function is also available as a global function named `mixin-exists()`.
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not an [`<ident-token>`], return false.
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* Return whether [resolving a mixin][] named `$name` returns null.
|
||||
|
||||
[resolving a mixin]: ../modules.md#resolving-a-member
|
||||
* Return whether [resolving a mixin] named `$name` returns null.
|
||||
|
||||
* Otherwise, if `$module` isn't a string, throw an error.
|
||||
|
||||
* Otherwise, let `use` be the `@use` rule in [the current source file][] whose
|
||||
* Otherwise, let `use` be the `@use` rule in [the current source file] whose
|
||||
namespace is equal to `$module`. If no such rule exists, throw an error.
|
||||
|
||||
* Return whether [`use`'s module][] contains a mixin named `$name`.
|
||||
* Return whether [`use`'s module] contains a mixin named `$name`.
|
||||
|
||||
### `module-functions()`
|
||||
|
||||
@ -225,6 +280,22 @@ This function is also available as a global function named `module-functions()`.
|
||||
* Return a map whose keys are the names of functions in [`use`'s module][] and
|
||||
whose values are the corresponding functions.
|
||||
|
||||
### `module-mixins()`
|
||||
|
||||
This is a new function in the `sass:meta` module.
|
||||
|
||||
```
|
||||
module-mixins($module)
|
||||
```
|
||||
|
||||
* If `$module` is not a string, throw an error.
|
||||
|
||||
* Let `use` be the `@use` rule in [the current source file] whose namespace is
|
||||
equal to `$module`. If no such rule exists, throw an error.
|
||||
|
||||
* Return a map whose keys are the quoted string names of mixins in
|
||||
[`use`'s module] and whose values are the corresponding mixins.
|
||||
|
||||
### `module-variables()`
|
||||
|
||||
```
|
||||
@ -261,6 +332,7 @@ This function is also available as a global function named `type-of()`.
|
||||
| Function | `"function"` |
|
||||
| List | `"list"` |
|
||||
| Map | `"map"` |
|
||||
| Mixin | `"mixin"` |
|
||||
| Null | `"null"` |
|
||||
| Number | `"number"` |
|
||||
| String | `"string"` |
|
||||
@ -275,6 +347,8 @@ This function is also available as a global function named `variable-exists()`.
|
||||
|
||||
* If `$name` is not a string, throw an error.
|
||||
|
||||
* If `$name` is not a [`PlainVariable`], return false.
|
||||
|
||||
* If `$module` is null:
|
||||
|
||||
* Return whether [resolving a variable][] named `$name` returns null.
|
||||
@ -288,6 +362,24 @@ This function is also available as a global function named `variable-exists()`.
|
||||
|
||||
## Mixins
|
||||
|
||||
### `apply()`
|
||||
|
||||
```
|
||||
apply($mixin, $args...)
|
||||
```
|
||||
|
||||
* If `$mixin` is not a [mixin], throw an error.
|
||||
|
||||
* If the current `@include` rule has a `ContentBlock` and `$mixin` doesn't
|
||||
accept a block, throw an error.
|
||||
|
||||
* Execute `$mixin` with the `ArgumentInvocation` `(...$args)`. Treat the
|
||||
`@include` rule that invoked `apply` as the `@include` rule that invoked
|
||||
`$mixin`.
|
||||
|
||||
> This ensures that any `@content` rules in `$mixin` will use `apply()`'s
|
||||
> `ContentBlock`.
|
||||
|
||||
### `load-css()`
|
||||
|
||||
```
|
||||
|
@ -426,13 +426,12 @@ and returns a member of type `type` or null.
|
||||
* If `name` is a plain `Identifier` or a `Variable` that's not a
|
||||
`NamespacedVariable`:
|
||||
|
||||
* Let `scope` be the [scope][] of the innermost block containing the current
|
||||
statement such that `scope` has a member of type `type` named `name`, or
|
||||
null if no such scope exists.
|
||||
* Let `scope` be the [current scope] or its innermost parent such that `scope`
|
||||
has a member of type `type` named `name`, or null if no such scope exists.
|
||||
|
||||
* If `scope` is not null, return `scope`'s value of type `type` named `name`.
|
||||
|
||||
[scope]: variables.md#scope
|
||||
[current scope]: spec.md#scope
|
||||
|
||||
* If `name` is a [`NamespacedIdentifier`](#syntax) of the form
|
||||
`namespace.raw-name` or a [`Variable`][] of the form `namespace.$raw-name`:
|
||||
|
34
spec/spec.md
34
spec/spec.md
@ -19,10 +19,14 @@ the language this way.
|
||||
## Table of Contents
|
||||
|
||||
* [Definitions](#definitions)
|
||||
* [Scope](#scope)
|
||||
* [Global Scope](#global-scope)
|
||||
* [Current Source File](#current-source-file)
|
||||
* [Current Configuration](#current-configuration)
|
||||
* [Current Import Context](#current-import-context)
|
||||
* [Current Module](#current-module)
|
||||
* [Procedures](#procedures)
|
||||
* [Running in a New Scope](#running-in-a-new-scope)
|
||||
* [Semantics](#semantics)
|
||||
* [Compiling a Path](#compiling-a-path)
|
||||
* [Compiling a String](#compiling-a-string)
|
||||
@ -30,6 +34,23 @@ the language this way.
|
||||
|
||||
## Definitions
|
||||
|
||||
### Scope
|
||||
|
||||
A *scope* is a mutable structure that contains:
|
||||
|
||||
* The scope's *variables*: a mapping from identifiers to SassScript values.
|
||||
* The scope's *mixins*: a mapping from identifiers to mixins.
|
||||
* The scope's *functions*: a mapping from identifiers to functions.
|
||||
* The scope's *parent*: a reference to another scope, which may be unset.
|
||||
|
||||
One scope at a time is designated the *current scope*. By default, this is the
|
||||
[global scope](#global-scope).
|
||||
|
||||
### Global Scope
|
||||
|
||||
The *global scope* is the scope shared among the top level of all Sass files. It
|
||||
has no parent.
|
||||
|
||||
### Current Source File
|
||||
|
||||
The *current source file* is the [source file][] that was passed to the
|
||||
@ -64,6 +85,19 @@ invocation of [Executing a File](#executing-a-file).
|
||||
> Because a module is only made immutable (other than its variables) when
|
||||
> execution has finished, the current module is always mutable.
|
||||
|
||||
## Procedures
|
||||
|
||||
### Running in a New Scope
|
||||
|
||||
To run a set of steps *in a new scope*:
|
||||
|
||||
* Let `parent` be the [current scope].
|
||||
|
||||
[current scope]: #scope
|
||||
|
||||
* Return the result of running the given steps with the current scope set to an
|
||||
empty scope with `parent` as its parent.
|
||||
|
||||
## Semantics
|
||||
|
||||
### Compiling a Path
|
||||
|
62
spec/types/functions.md
Normal file
62
spec/types/functions.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Functions
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Types](#types)
|
||||
* [Operations](#operations)
|
||||
* [Equality](#equality)
|
||||
* [Serialization](#serialization)
|
||||
|
||||
## Types
|
||||
|
||||
The value type known as a "function" is a procedure that takes an
|
||||
`ArgumentInvocation` `args` and returns a SassScript value. Each function has a
|
||||
string name.
|
||||
|
||||
> The specific details of executing this procedure differ depending on where and
|
||||
> how the function is defined.
|
||||
|
||||
### Operations
|
||||
|
||||
A function follows the default behavior of all SassScript operations, except
|
||||
that equality is defined as below.
|
||||
|
||||
#### Equality
|
||||
|
||||
Functions use reference equality: two function values are equal only if they
|
||||
refer to the exact same instance of the same procedure.
|
||||
|
||||
> If the same file were to be imported multiple times, Sass would create a new
|
||||
> function value for each `@function` rule each time the file is imported.
|
||||
> Because a new function value has been created, although the name, body, and
|
||||
> source span of a given function from the file would be the same between
|
||||
> imports, the values would not be equal because they refer to different
|
||||
> instances. Functions pre-defined by the Sass language are instatiated at most
|
||||
> once during the entire evaluation of a program.
|
||||
>
|
||||
> As an example, if we declare two functions:
|
||||
>
|
||||
> ```scss
|
||||
> @function foo() {
|
||||
> @return red;
|
||||
> }
|
||||
>
|
||||
> $a: meta.get-function(foo);
|
||||
>
|
||||
> @function foo {
|
||||
> @return red;
|
||||
> }
|
||||
>
|
||||
> $b: meta.get-function(foo);
|
||||
> ```
|
||||
>
|
||||
> Although every aspect of the two functions is the same, `$a != $b`, because
|
||||
> they refer to separate function values.
|
||||
|
||||
### Serialization
|
||||
|
||||
To serialize a function value:
|
||||
|
||||
* If the value is not being inspected, throw an error.
|
||||
|
||||
* Otherwise, emit `'get-function("'`, then the function's name, then `'")'`.
|
63
spec/types/mixins.md
Normal file
63
spec/types/mixins.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Mixins
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Types](#types)
|
||||
* [Operations](#operations)
|
||||
* [Equality](#equality)
|
||||
* [Serialization](#serialization)
|
||||
|
||||
## Types
|
||||
|
||||
The value type known as a "mixin" is a procedure that takes an
|
||||
`ArgumentInvocation` `args` and returns nothing. Each mixin has a string name
|
||||
and a boolean that indicates whether or not it accepts a content block.
|
||||
|
||||
> The specific details of executing this procedure differ depending on where and
|
||||
> how the mixin is defined. A mixin will typically add nodes to the CSS
|
||||
> stylesheet.
|
||||
|
||||
### Operations
|
||||
|
||||
A mixin follows the default behavior of all SassScript operations, except that
|
||||
equality is defined as below.
|
||||
|
||||
#### Equality
|
||||
|
||||
Mixins use reference equality: two mixin values are equal only if they refer to
|
||||
the exact same instance of the same procedure.
|
||||
|
||||
> If the same file were to be imported multiple times, Sass would create a new
|
||||
> mixin value for each `@mixin` rule each time the file is imported. Because a
|
||||
> new mixin value has been created, although the name, body, and source span of
|
||||
> a given mixin from the file would be the same between imports, the values
|
||||
> would not be equal because they refer to different instances. Mixins
|
||||
> pre-defined by the Sass language are instatiated at most once during the
|
||||
> entire evaluation of a program.
|
||||
>
|
||||
> As an example, if we declare two mixins:
|
||||
>
|
||||
> ```scss
|
||||
> @mixin foo {
|
||||
> color: red;
|
||||
> }
|
||||
>
|
||||
> $a: meta.get-mixin(foo);
|
||||
>
|
||||
> @mixin foo {
|
||||
> color: red;
|
||||
> }
|
||||
>
|
||||
> $b: meta.get-mixin(foo);
|
||||
> ```
|
||||
>
|
||||
> Although every aspect of the two mixins is the same, `$a != $b`, because they
|
||||
> refer to separate mixin values.
|
||||
|
||||
### Serialization
|
||||
|
||||
To serialize a mixin value:
|
||||
|
||||
* If the value is not being inspected, throw an error.
|
||||
|
||||
* Otherwise, emit `'get-mixin("'`, then the mixin's name, then `'")'`.
|
@ -3,9 +3,6 @@
|
||||
## Table of Contents
|
||||
|
||||
* [Syntax](#syntax)
|
||||
* [Definitions](#definitions)
|
||||
* [Scope](#scope)
|
||||
* [Global Scope](#global-scope)
|
||||
* [Semantics](#semantics)
|
||||
* [Executing a Variable Declaration](#executing-a-variable-declaration)
|
||||
* [Evaluating a Variable](#evaluating-a-variable)
|
||||
@ -27,18 +24,6 @@ the `.$` in `NamespacedVariable`. Each of `!global` and `!default` is allowed
|
||||
at most once in `VariableDeclaration`. As with all statements, a
|
||||
`VariableDeclaration` must be separated from other statements with a semicolon.
|
||||
|
||||
## Definitions
|
||||
|
||||
### Scope
|
||||
|
||||
A *scope* is a mapping from variable names to values. Every block of statements
|
||||
delimited by `{` and `}` in SCSS or by indentation in the indented syntax has an
|
||||
associated scope.
|
||||
|
||||
### Global Scope
|
||||
|
||||
The *global scope* is the scope shared among the top level of all Sass files.
|
||||
|
||||
## Semantics
|
||||
|
||||
### Executing a Variable Declaration
|
||||
@ -107,8 +92,10 @@ To execute a `VariableDeclaration` `declaration`:
|
||||
|
||||
> This also overrides the previous definition.
|
||||
|
||||
* Otherwise, if `resolved` is null, get the innermost block containing
|
||||
`declaration` and set its scope's variable `name` to `value`.
|
||||
* Otherwise, if `resolved` is null, set the [current scope]'s variable `name` to
|
||||
`value`.
|
||||
|
||||
[current scope]: spec.md#scope
|
||||
|
||||
* Otherwise, set `resolved`'s value to `value`.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user