diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f3bb83f..d0312000 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: 'lts/*' check-latest: true @@ -46,12 +46,12 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: ${{ matrix.node-version }} check-latest: true - - uses: dart-lang/setup-dart@v1 + - uses: dart-lang/setup-dart@v1.8.1 with: {sdk: stable} - run: dart --version @@ -89,10 +89,10 @@ jobs: node_version: lts/-2 steps: - - uses: actions/checkout@v6 - - uses: dart-lang/setup-dart@v1 + - uses: actions/checkout@v7 + - uses: dart-lang/setup-dart@v1.8.1 with: {sdk: stable} - - uses: actions/setup-node@v6 + - uses: actions/setup-node@v7 with: {node-version: "${{ matrix.node_version }}"} - name: Check out Dart Sass diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e82f96ec..9e664560 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 + - uses: actions/checkout@v7 + - uses: actions/setup-node@v7 with: node-version: 'lts/*' check-latest: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cdd7f5c..fa700ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,172 @@ +## 1.104.1 + +* Fix a bug where loud comments before `@use` rules could be emitted multiple + times under certain circumstances. + +* Fix a bug in which an invalid selector at the end of an indented syntax + stylesheet would cause the parser to crash instead of emitting a useful error + message. + +### Command Line Interface + +* Many-to-many compilations no longer compile any files that appear in the + output directory if the output directory is also within the source directory. + This fixes a bug where `--watch` mode could enter an infinite loop recompiling + the same CSS file over and over. + +* Sass now sets the modification time of output files to the time compilation + *started* rather than the time it *ended*. This ensures that, if a source file + is modified during compilation, `--watch` and `--update` mode will recompile + the outputs to include the new source file contents. + +## 1.104.0 + +* **Potentially breaking compatibility fix:** Colors now convert the special + values NaN and negative zero, as well as infinity and negative infinity for + polar-hue channels, to 0 as per the CSS spec. + +* The special value negative zero is now serialized as `-0` instead of `0` for + greater compatibility when using it in CSS calculations. + +## 1.103.1 + +* No user-visible changes. + +## 1.103.0 + +* **Potentially breaking compatibility fix:** Colors now preserve "analogous + sets" of missing channels during conversions, per the CSS spec. For example, + `color.to-space(lch(50% none none), lab)` now returns `lab(50% none none)` + instead of `lab(50% 0 0)`. + +## 1.102.0 + +* Use the 2.4 gamma transfer function for rec2020, as specified by the latest + draft of CSS Color 4. + +## 1.101.7 + +* No user-visible changes. + +## 1.101.6 + +* No user-visible changes. + +## 1.101.5 + +* No user-visible changes. + +## 1.101.4 + +* Avoid emitting `rgb()` or `rgba()` functions with non-percent decimal + channels. Older browsers only support integer values or (potentially decimal) + percentages for these functions, so in order to preserve + backwards-compatibility while retaining full precision for modern browsers, + legacy colors that contain at least one non-integer channel will now use + percentages for their channels (for example, `rgb(0%, 100%, 50%)` rather than + `rgb(0, 255, 127.5)`). + +* Fix a bug where the values of plain-CSS `if()` expressions were emitted using + their `meta.inspect()` format rather than their CSS serialization format. + +## 1.101.3 + +* No user-visible changes. + +## 1.101.2 + +* Fix a bug where the deprecation warning for vendor-prefixed `expression()` + functions would incorrectly indicate whether or not the function would be + invalid Sass in Dart Sass 2.0.0. + +## 1.101.1 + +* Sass stack trace entries are now always either absolute URLs, absolute paths, + or paths relative to the current working directory. Previously, if a + stylesheet was loaded using a relative URL (as from a load path), that + relative URL was listed even if it couldn't be resolved relative to the + current working directory. However, this created potential ambiguities, so + this behavior has been removed. + +### Command Line Interface + +* `--watch` mode now handles atomically-written files more gracefully. + +## 1.101.0 + +* **Potentially breaking bug fix:** The Node package importer now properly + supports resolving import-only variants of Sass files declared in the + `exports`, `sass`, and `style` fields of `package.json`. Previously, these + files were ignored even when loaded via `@import`, so any code relying on + loading module-system-only files this way may break. + +## 1.100.0 + +* Writing two compound selectors adjacent to one another without any whitespace + between them, such as `[class]a`, is now deprecated. This was always an error + in CSS and Sass only supported it by mistake. + + See [the Sass website](https://sass-lang.com/d/adjacent-compounds) for + details. + +## 1.99.0 + +* Add support for parent selectors (`&`) at the root of the document. These are + emitted as-is in the CSS output, where they're interpreted as [the scoping + root]. + + [the scoping root]: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Nesting_selector#using_outside_nested_rule + +* User-defined functions named `calc` or `clamp` are no longer forbidden. If + such a function exists without a namespace in the current module, it will be + used instead of the built-in `calc()` or `clamp()` function. + +* User-defined functions whose names begin with `-` and end with `-expression`, + `-url`, `-and`, `-or`, or `-not` are no longer forbidden. These were + originally intended to match vendor prefixes, but in practice no vendor + prefixes for these functions ever existed in real browsers. + +* User-defined functions named `EXPRESSION`, `URL`, and `ELEMENT`, those that + begin with `-` and end with `-ELEMENT`, as well as the same names with some + lowercase letters are now deprecated, These are names conflict with plain CSS + functions that have special syntax. + + See [the Sass website](https://sass-lang.com/d/function-name) for details. + +* In a future release, calls to functions whose names begin with `-` and end + with `-expression` and `-url` will no longer have special parsing. For now, + these calls are deprecated if their behavior will change in the future. + + See [the Sass website](https://sass-lang.com/d/function-name) for details. + +* Calls to functions whose names begin with `-` and end with `-progid:...` are + deprecated. + + See [the Sass website](https://sass-lang.com/d/function-name) for details. + +## 1.98.0 + +### Command-Line Interface + +* Gracefully handle dependency loops in `--watch` mode. + +### Dart API + +* Add a `const Logger.defaultLogger` field. This provides a logger that emits to + standard error or the browser console, but automatically chooses whether to + use terminal colors. + +### JavaScript API + +* Fix a crash when manually constructing a `SassCalculation` for `'calc'` with + an argument that can't be simplified. + +* Properly emit deprecation warnings as text rather than `StringBuffer` objects + when running in a browser. + +* Emit colored warnings and other messages on the console when running in a + browser. + ## 1.97.3 * Fix a bug where nesting an at-rule within multiple style rules in plain CSS diff --git a/lib/src/compiler/async.ts b/lib/src/compiler/async.ts index 3fcd9025..09f99287 100644 --- a/lib/src/compiler/async.ts +++ b/lib/src/compiler/async.ts @@ -96,7 +96,7 @@ export class AsyncCompiler { /** Guards against using a disposed compiler. */ private throwIfDisposed(): void { if (this.disposed) { - throw utils.compilerError('Async compiler has already been disposed.'); + throw utils.compilerError('Async compiler has already been disposed'); } } @@ -161,7 +161,7 @@ export class AsyncCompiler { if (flag !== initFlag) { throw utils.compilerError( 'AsyncCompiler can not be directly constructed. ' + - 'Please use `sass.initAsyncCompiler()` instead.', + 'Please use `sass.initAsyncCompiler()` instead', ); } this.stderr$.subscribe(data => process.stderr.write(data)); diff --git a/lib/src/compiler/sync.ts b/lib/src/compiler/sync.ts index 66fdaf8e..652b96a1 100644 --- a/lib/src/compiler/sync.ts +++ b/lib/src/compiler/sync.ts @@ -153,7 +153,7 @@ export class Compiler { for (;;) { if (!this.yield()) { - throw utils.compilerError('Embedded compiler exited unexpectedly.'); + throw utils.compilerError('Embedded compiler exited unexpectedly'); } if (error) throw error; @@ -167,7 +167,7 @@ export class Compiler { /** Guards against using a disposed compiler. */ private throwIfDisposed(): void { if (this.disposed) { - throw utils.compilerError('Sync compiler has already been disposed.'); + throw utils.compilerError('Sync compiler has already been disposed'); } } @@ -176,7 +176,7 @@ export class Compiler { if (flag !== initFlag) { throw utils.compilerError( 'Compiler can not be directly constructed. ' + - 'Please use `sass.initAsyncCompiler()` instead.', + 'Please use `sass.initCompiler()` instead', ); } this.stderr$.subscribe(data => process.stderr.write(data)); diff --git a/lib/src/compiler/utils.ts b/lib/src/compiler/utils.ts index 272aa0c4..288538b5 100644 --- a/lib/src/compiler/utils.ts +++ b/lib/src/compiler/utils.ts @@ -183,7 +183,7 @@ export function handleCompileResponse( } else if (response.result.case === 'failure') { throw new Exception(response.result.value); } else { - throw utils.compilerError('Compiler sent empty CompileResponse.'); + throw utils.compilerError('Compiler sent empty CompileResponse'); } } diff --git a/lib/src/deprotofy-span.ts b/lib/src/deprotofy-span.ts index bd32762f..8e2b8867 100644 --- a/lib/src/deprotofy-span.ts +++ b/lib/src/deprotofy-span.ts @@ -14,20 +14,20 @@ export function deprotofySourceSpan(buffer: proto.SourceSpan): SourceSpan { const text = buffer.text; if (buffer.start === undefined) { - throw compilerError('Expected SourceSpan to have start.'); + throw compilerError('Expected SourceSpan to have start'); } let end; if (buffer.end === undefined) { if (text !== '') { - throw compilerError('Expected SourceSpan text to be empty.'); + throw compilerError('Expected SourceSpan text to be empty'); } else { end = buffer.start; } } else { end = buffer.end; if (end.offset < buffer.start.offset) { - throw compilerError('Expected SourceSpan end to be after start.'); + throw compilerError('Expected SourceSpan end to be after start'); } } diff --git a/lib/src/protofier.ts b/lib/src/protofier.ts index c06dfdd6..f772901f 100644 --- a/lib/src/protofier.ts +++ b/lib/src/protofier.ts @@ -324,7 +324,7 @@ export class Protofier { }); default: - throw utils.compilerError(`Unknown color space "${color.space}".`); + throw utils.compilerError(`Unknown color space "${color.space}"`); } } @@ -394,7 +394,7 @@ export class Protofier { case 'hostFunction': throw utils.compilerError( - 'The compiler may not send Value.host_function.', + 'The compiler may not send Value.host_function', ); case 'compilerMixin': @@ -454,7 +454,7 @@ export class Protofier { case 'calc': if (calculation.arguments.length !== 1) { throw utils.compilerError( - 'Value.Calculation.arguments must have exactly one argument for calc().', + 'Value.Calculation.arguments must have exactly one argument for calc()', ); } return SassCalculation.calc( @@ -466,7 +466,7 @@ export class Protofier { calculation.arguments.length > 3 ) { throw utils.compilerError( - 'Value.Calculation.arguments must have 1 to 3 arguments for clamp().', + 'Value.Calculation.arguments must have 1 to 3 arguments for clamp()', ); } return SassCalculation.clamp( @@ -481,7 +481,7 @@ export class Protofier { case 'min': if (calculation.arguments.length === 0) { throw utils.compilerError( - 'Value.Calculation.arguments must have at least 1 argument for min().', + 'Value.Calculation.arguments must have at least 1 argument for min()', ); } return SassCalculation.min( @@ -490,7 +490,7 @@ export class Protofier { case 'max': if (calculation.arguments.length === 0) { throw utils.compilerError( - 'Value.Calculation.arguments must have at least 1 argument for max().', + 'Value.Calculation.arguments must have at least 1 argument for max()', ); } return SassCalculation.max( @@ -498,7 +498,7 @@ export class Protofier { ); default: throw utils.compilerError( - `Value.Calculation.name "${calculation.name}" is not a recognized calculation type.`, + `Value.Calculation.name "${calculation.name}" is not a recognized calculation type`, ); } } diff --git a/lib/src/utils.ts b/lib/src/utils.ts index 1dbbd75c..308f56a8 100644 --- a/lib/src/utils.ts +++ b/lib/src/utils.ts @@ -73,7 +73,7 @@ export function compilerError(message: string): Error { * been included but was not. */ export function mandatoryError(field: string): Error { - return compilerError(`Missing mandatory field ${field}`); + return compilerError(`Missing mandatory field ${field}.`); } /** Constructs a host-caused Error. */ diff --git a/lib/src/value/color.ts b/lib/src/value/color.ts index 5018c220..bc020b26 100644 --- a/lib/src/value/color.ts +++ b/lib/src/value/color.ts @@ -86,10 +86,7 @@ type PolarColorSpace = ColorSpaceHsl | ColorSpaceHwb | ColorSpaceLch; * colors. */ type HueInterpolationMethod = - | 'decreasing' - | 'increasing' - | 'longer' - | 'shorter'; + 'decreasing' | 'increasing' | 'longer' | 'shorter'; /** * Methods by which colors in bounded spaces can be mapped to within their @@ -116,30 +113,19 @@ function getColorSpace(options: ChannelOptions): KnownColorSpace { throw valueError('No color space found'); } -/** - * Convert from the ColorJS representation of a missing component (`NaN`) to - * `null`. - */ -function NaNtoNull(val: number): number | null { - return Number.isNaN(val) ? null : val; -} - -/** - * Convert from the ColorJS representation of a missing component (`NaN`) to - * `0`. - */ -function NaNtoZero(val: number): number { - return Number.isNaN(val) ? 0 : val; +/** Convert from sRGB (0-1) to RGB (0-255) units. */ +function coordToRgb(val: number | null): number | null { + return val === null ? val : val * 255; } -/** Convert from sRGB (0-1) to RGB (0-255) units. */ -function coordToRgb(val: number): number { - return val * 255; +/** Convert from RGB (0-255) to sRGB (0-1) units. */ +function rgbToCoord(val: number | null): number | null { + return val === null ? val : val / 255; } /** Normalize `hue` values to be within the range `[0, 360)`. */ -function normalizeHue(val: number): number { - return positiveMod(val, 360); +function normalizeHue(val: number | null): number | null { + return val === null ? val : positiveMod(val, 360); } /** @@ -248,7 +234,7 @@ function validateChannelInSpace( } if (!valid) { throw valueError( - `Unknown channel name "${channel}" for color space "${space}".`, + `Unknown channel name "${channel}" for color space "${space}"`, ); } } @@ -267,24 +253,24 @@ function isPolarColorSpace(space: KnownColorSpace): space is PolarColorSpace { } /** - * Convert from ColorJS coordinates (which use `NaN` for missing components, and - * a range of `0-1` for `rgb` channel values) to Sass Color coordinates (which - * use `null` for missing components, and a range of `0-255` for `rgb` channel - * values). + * Convert from ColorJS coordinates (which use a range of `0-1` for `rgb` + * channel values) to Sass Color coordinates (which use a range of `0-255` for + * `rgb` channel values). */ function decodeCoordsFromColorJs( - coords: [number, number, number], // ColorJS coordinates + coords: [number | null, number | null, number | null], // ColorJS coordinates isRgb = false, // Whether this color is in the `rgb` color space ): [number | null, number | null, number | null] { let newCoords = coords; // If this color is in the `rgb` space, convert channel values to `0-255` - if (isRgb) newCoords = newCoords.map(coordToRgb) as [number, number, number]; - // Convert `NaN` values to `null` - return newCoords.map(NaNtoNull) as [ - number | null, - number | null, - number | null, - ]; + if (isRgb) { + newCoords = newCoords.map(coordToRgb) as [ + number | null, + number | null, + number | null, + ]; + } + return newCoords; } /** Returns `true` if `val` is a `number` or `null`. */ @@ -393,9 +379,9 @@ export class SassColor extends Value { const space = options.space ?? getColorSpace(options); this.setChannelIds(space); if (space === 'rgb') this.isRgb = true; - let alpha: number; + let alpha: number | null; if (options.alpha === null) { - alpha = NaN; + alpha = null; } else if (options.alpha === undefined) { alpha = 1; } else { @@ -405,14 +391,18 @@ export class SassColor extends Value { switch (space) { case 'rgb': case 'srgb': { - const red = options.red ?? NaN; - const green = options.green ?? NaN; - const blue = options.blue ?? NaN; + const red = options.red ?? null; + const green = options.green ?? null; + const blue = options.blue ?? null; if (this.isRgb) { this.color = new Color({ spaceId: encodeSpaceForColorJs(space), // convert from 0-255 to 0-1 - coords: [red / 255, green / 255, blue / 255], + coords: [red, green, blue].map(rgbToCoord) as [ + number | null, + number | null, + number | null, + ], alpha, }); } else { @@ -434,21 +424,23 @@ export class SassColor extends Value { this.color = new Color({ spaceId: encodeSpaceForColorJs(space), coords: [ - options.red ?? NaN, - options.green ?? NaN, - options.blue ?? NaN, + options.red ?? null, + options.green ?? null, + options.blue ?? null, ], alpha, }); break; case 'hsl': { - let hue = normalizeHue(options.hue ?? NaN); - let saturation = options.saturation ?? NaN; - const lightness = options.lightness ?? NaN; - if (!Number.isNaN(saturation) && fuzzyLessThan(saturation, 0)) { + let hue = normalizeHue(options.hue ?? null); + let saturation = options.saturation ?? null; + const lightness = options.lightness ?? null; + if (saturation !== null && fuzzyLessThan(saturation, 0)) { saturation = Math.abs(saturation); - hue = (hue + 180) % 360; + if (hue !== null) { + hue = (hue + 180) % 360; + } } this.color = new Color({ @@ -460,9 +452,9 @@ export class SassColor extends Value { } case 'hwb': { - const hue = normalizeHue(options.hue ?? NaN); - const whiteness = options.whiteness ?? NaN; - const blackness = options.blackness ?? NaN; + const hue = normalizeHue(options.hue ?? null); + const whiteness = options.whiteness ?? null; + const blackness = options.blackness ?? null; this.color = new Color({ spaceId: encodeSpaceForColorJs(space), coords: [hue, whiteness, blackness], @@ -473,9 +465,9 @@ export class SassColor extends Value { case 'lab': case 'oklab': { - const lightness = options.lightness ?? NaN; - const a = options.a ?? NaN; - const b = options.b ?? NaN; + const lightness = options.lightness ?? null; + const a = options.a ?? null; + const b = options.b ?? null; this.color = new Color({ spaceId: encodeSpaceForColorJs(space), coords: [lightness, a, b], @@ -486,12 +478,14 @@ export class SassColor extends Value { case 'lch': case 'oklch': { - const lightness = options.lightness ?? NaN; - let chroma = options.chroma ?? NaN; - let hue = normalizeHue(options.hue ?? NaN); - if (!Number.isNaN(chroma) && fuzzyLessThan(chroma, 0)) { + const lightness = options.lightness ?? null; + let chroma = options.chroma ?? null; + let hue = normalizeHue(options.hue ?? null); + if (chroma !== null && fuzzyLessThan(chroma, 0)) { chroma = Math.abs(chroma); - hue = (hue + 180) % 360; + if (hue !== null) { + hue = (hue + 180) % 360; + } } this.color = new Color({ @@ -507,23 +501,16 @@ export class SassColor extends Value { case 'xyz-d50': this.color = new Color({ spaceId: encodeSpaceForColorJs(space), - coords: [options.x ?? NaN, options.y ?? NaN, options.z ?? NaN], + coords: [options.x ?? null, options.y ?? null, options.z ?? null], alpha, }); break; } - - // @TODO Waiting on new release of ColorJS that includes allowing `alpha` - // to be `NaN` on initial construction. - // Fixed in: https://github.com/LeaVerou/color.js/commit/08b39c180565ae61408ad737d91bd71a1f79d3df - if (Number.isNaN(alpha)) { - this.color.alpha = NaN; - } } /** This color's alpha channel, between `0` and `1`. */ get alpha(): number { - return NaNtoZero(this.color.alpha); + return this.color.alpha ?? 0; } /** The name of this color's color space. */ @@ -548,9 +535,13 @@ export class SassColor extends Value { get channelsOrNull(): List { let coords = this.color.coords; if (this.space === 'rgb') { - coords = coords.map(coordToRgb) as [number, number, number]; + coords = coords.map(coordToRgb) as [ + number | null, + number | null, + number | null, + ]; } - return List(coords.map(NaNtoNull)); + return List(coords); } /** @@ -562,9 +553,13 @@ export class SassColor extends Value { get channels(): List { let coords = this.color.coords; if (this.space === 'rgb') { - coords = coords.map(coordToRgb) as [number, number, number]; + coords = coords.map(coordToRgb) as [ + number | null, + number | null, + number | null, + ]; } - return List(coords.map(NaNtoZero)); + return List(coords.map(val => val ?? 0)); } assertColor(): SassColor { @@ -626,7 +621,7 @@ export class SassColor extends Value { channel(channel: ChannelNameXyz, options: {space: ColorSpaceXyz}): number; channel(channel: ChannelName, options?: {space: KnownColorSpace}): number { if (channel === 'alpha') return this.alpha; - let val: number; + let val: number | null; const space = options?.space ?? this.space; validateChannelInSpace(channel, space); if (options?.space) { @@ -641,7 +636,7 @@ export class SassColor extends Value { }); } if (space === 'rgb') val = coordToRgb(val); - return NaNtoZero(val); + return val ?? 0; } /** @@ -651,13 +646,13 @@ export class SassColor extends Value { * [missing channel]: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#missing_color_components */ isChannelMissing(channel: ChannelName): boolean { - if (channel === 'alpha') return Number.isNaN(this.color.alpha); + if (channel === 'alpha') return this.color.alpha === null; validateChannelInSpace(channel, this.space); - return Number.isNaN( + return ( this.color.get({ space: this.color.spaceId, coordId: encodeChannelForColorJs(channel), - }), + }) === null ); } @@ -748,7 +743,7 @@ export class SassColor extends Value { if (weight < 0 || weight > 1) { throw valueError( - `Expected \`weight\` between \`0\` and \`1\`, received \`${weight}\`.`, + `Expected \`weight\` between \`0\` and \`1\`, received \`${weight}\``, ); } @@ -764,7 +759,7 @@ export class SassColor extends Value { [this.channel0Id]: coords[0], [this.channel1Id]: coords[1], [this.channel2Id]: coords[2], - alpha: NaNtoNull(this.color.alpha), + alpha: this.color.alpha, }); } @@ -932,11 +927,11 @@ export class SassColor extends Value { coords = this.color .to('srgb') .coords.map(coordToRgb) - .map(fuzzyRound) as [number, number, number]; + .map(fuzzyRound) as [number | null, number | null, number | null]; otherCoords = other.color .to('srgb') .coords.map(coordToRgb) - .map(fuzzyRound) as [number, number, number]; + .map(fuzzyRound) as [number | null, number | null, number | null]; } return ( fuzzyEquals(coords[0], otherCoords[0]) && diff --git a/lib/src/value/index.ts b/lib/src/value/index.ts index b77c3395..793d04db 100644 --- a/lib/src/value/index.ts +++ b/lib/src/value/index.ts @@ -86,7 +86,7 @@ export abstract class Value implements ValueObject { } if (Math.abs(index) > this.lengthAsList) { throw valueError( - `Invalid index ${sassIndex} for a list with ${this.lengthAsList} elements.`, + `Invalid index ${sassIndex} for a list with ${this.lengthAsList} elements`, name, ); } diff --git a/lib/src/value/number.ts b/lib/src/value/number.ts index 52397e57..298c99ac 100644 --- a/lib/src/value/number.ts +++ b/lib/src/value/number.ts @@ -596,7 +596,7 @@ export class SassNumber extends Value { } if (!otherHasUnits) { - return valueError(`Expected ${this} to have no units.`, params.name); + return valueError(`Expected ${this} to have no units`, params.name); } // For single numerators, throw a detailed error with info about which unit @@ -607,7 +607,7 @@ export class SassNumber extends Value { return valueError( `Expected ${this} to have a single ${type} unit (${unitsByType[ type - ].join(', ')}).`, + ].join(', ')})`, params.name, ); } @@ -622,7 +622,7 @@ export class SassNumber extends Value { newNumerators, newDenominators, )}` - }.`, + }`, params.name, ); }; diff --git a/lib/src/value/utils.ts b/lib/src/value/utils.ts index 7844c810..8f054675 100644 --- a/lib/src/value/utils.ts +++ b/lib/src/value/utils.ts @@ -16,7 +16,10 @@ export const precision = 10; const epsilon = 10 ** (-precision - 1); /** Whether `num1` and `num2` are equal within `epsilon`. */ -export function fuzzyEquals(num1: number, num2: number): boolean { +export function fuzzyEquals(num1: number | null, num2: number | null): boolean { + if (num1 === null || num2 === null) { + return false; + } return Math.abs(num1 - num2) < epsilon; } @@ -25,10 +28,8 @@ export function fuzzyEquals(num1: number, num2: number): boolean { * * Two numbers that `fuzzyEquals` each other must have the same hash code. */ -export function fuzzyHashCode(num: number): number { - return !isFinite(num) || isNaN(num) - ? hash(num) - : hash(Math.round(num / epsilon)); +export function fuzzyHashCode(num: number | null): number { + return num === null ? hash(num) : hash(Math.round(num / epsilon)); } /** Whether `num1` < `num2`, within `epsilon`. */ @@ -72,7 +73,10 @@ export function fuzzyAsInt(num: number): number | null { * * If `num` `fuzzyEquals` `x.5`, rounds away from zero. */ -export function fuzzyRound(num: number): number { +export function fuzzyRound(num: number | null): number | null { + if (num === null) { + return num; + } if (num > 0) { return fuzzyLessThan(num % 1, 0.5) ? Math.floor(num) : Math.ceil(num); } else { diff --git a/npm/all-unknown/package.json b/npm/all-unknown/package.json index eeb50dd4..c7535f96 100644 --- a/npm/all-unknown/package.json +++ b/npm/all-unknown/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-all-unknown", - "version": "1.97.3", + "version": "1.104.1", "description": "The pure js optional dependency for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -12,6 +12,6 @@ "!x64" ], "dependencies": { - "sass": "1.97.3" + "sass": "1.104.1" } } diff --git a/npm/android-arm/package.json b/npm/android-arm/package.json index 887f7101..6b68cdcc 100644 --- a/npm/android-arm/package.json +++ b/npm/android-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm", - "version": "1.97.3", + "version": "1.104.1", "description": "The android-arm binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/android-arm64/package.json b/npm/android-arm64/package.json index ce0c813f..e0563a9f 100644 --- a/npm/android-arm64/package.json +++ b/npm/android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm64", - "version": "1.97.3", + "version": "1.104.1", "description": "The android-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/android-riscv64/package.json b/npm/android-riscv64/package.json index 21a36537..df860337 100644 --- a/npm/android-riscv64/package.json +++ b/npm/android-riscv64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-riscv64", - "version": "1.97.3", + "version": "1.104.1", "description": "The android-riscv64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/android-x64/package.json b/npm/android-x64/package.json index 8a27cb6f..9d89dff6 100644 --- a/npm/android-x64/package.json +++ b/npm/android-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-x64", - "version": "1.97.3", + "version": "1.104.1", "description": "The android-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json index 819cabb8..1164a521 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-arm64", - "version": "1.97.3", + "version": "1.104.1", "description": "The darwin-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json index eb4eda66..d123e1e8 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-x64", - "version": "1.97.3", + "version": "1.104.1", "description": "The darwin-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-arm/package.json b/npm/linux-arm/package.json index 7bf32cdb..467d3182 100644 --- a/npm/linux-arm/package.json +++ b/npm/linux-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-arm binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-arm64/package.json b/npm/linux-arm64/package.json index f6255195..7ab022a1 100644 --- a/npm/linux-arm64/package.json +++ b/npm/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-arm/package.json b/npm/linux-musl-arm/package.json index 9d7ca4c5..6e4acb62 100644 --- a/npm/linux-musl-arm/package.json +++ b/npm/linux-musl-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-musl-arm", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-musl-arm binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-arm64/package.json b/npm/linux-musl-arm64/package.json index c64412da..170dc11d 100644 --- a/npm/linux-musl-arm64/package.json +++ b/npm/linux-musl-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-musl-arm64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-musl-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-riscv64/package.json b/npm/linux-musl-riscv64/package.json index 23077b92..3835436e 100644 --- a/npm/linux-musl-riscv64/package.json +++ b/npm/linux-musl-riscv64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-musl-riscv64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-musl-riscv64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-x64/package.json b/npm/linux-musl-x64/package.json index 86e9e070..396b113d 100644 --- a/npm/linux-musl-x64/package.json +++ b/npm/linux-musl-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-musl-x64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-musl-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-riscv64/package.json b/npm/linux-riscv64/package.json index 8cac976c..5fad3080 100644 --- a/npm/linux-riscv64/package.json +++ b/npm/linux-riscv64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-riscv64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-riscv64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-x64/package.json b/npm/linux-x64/package.json index 62f36448..e66d4d2a 100644 --- a/npm/linux-x64/package.json +++ b/npm/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-x64", - "version": "1.97.3", + "version": "1.104.1", "description": "The linux-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/unknown-all/package.json b/npm/unknown-all/package.json index 937e94b3..fc294769 100644 --- a/npm/unknown-all/package.json +++ b/npm/unknown-all/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-unknown-all", - "version": "1.97.3", + "version": "1.104.1", "description": "The pure js optional dependency for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -12,6 +12,6 @@ "!win32" ], "dependencies": { - "sass": "1.97.3" + "sass": "1.104.1" } } diff --git a/npm/win32-arm64/package.json b/npm/win32-arm64/package.json index ac712711..ed480467 100644 --- a/npm/win32-arm64/package.json +++ b/npm/win32-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-arm64", - "version": "1.97.3", + "version": "1.104.1", "description": "The win32-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/win32-x64/package.json b/npm/win32-x64/package.json index 03509fb7..132114a2 100644 --- a/npm/win32-x64/package.json +++ b/npm/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-x64", - "version": "1.97.3", + "version": "1.104.1", "description": "The win32-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/package.json b/package.json index af5b570e..d595f8c2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "sass-embedded", - "version": "1.97.3", + "version": "1.104.1", "protocol-version": "3.2.0", - "compiler-version": "1.97.3", + "compiler-version": "1.104.1", "description": "Node.js library that communicates with Embedded Dart Sass using the Embedded Sass protocol", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -21,7 +21,7 @@ "dist/**/*" ], "engines": { - "node": ">=16.0.0" + "node": ">=20.19.0" }, "bin": { "sass": "dist/bin/sass.js" @@ -41,29 +41,29 @@ "test": "jest" }, "optionalDependencies": { - "sass-embedded-all-unknown": "1.97.3", - "sass-embedded-android-arm": "1.97.3", - "sass-embedded-android-arm64": "1.97.3", - "sass-embedded-android-riscv64": "1.97.3", - "sass-embedded-android-x64": "1.97.3", - "sass-embedded-darwin-arm64": "1.97.3", - "sass-embedded-darwin-x64": "1.97.3", - "sass-embedded-linux-arm": "1.97.3", - "sass-embedded-linux-arm64": "1.97.3", - "sass-embedded-linux-musl-arm": "1.97.3", - "sass-embedded-linux-musl-arm64": "1.97.3", - "sass-embedded-linux-musl-riscv64": "1.97.3", - "sass-embedded-linux-musl-x64": "1.97.3", - "sass-embedded-linux-riscv64": "1.97.3", - "sass-embedded-linux-x64": "1.97.3", - "sass-embedded-unknown-all": "1.97.3", - "sass-embedded-win32-arm64": "1.97.3", - "sass-embedded-win32-x64": "1.97.3" + "sass-embedded-all-unknown": "1.104.1", + "sass-embedded-android-arm": "1.104.1", + "sass-embedded-android-arm64": "1.104.1", + "sass-embedded-android-riscv64": "1.104.1", + "sass-embedded-android-x64": "1.104.1", + "sass-embedded-darwin-arm64": "1.104.1", + "sass-embedded-darwin-x64": "1.104.1", + "sass-embedded-linux-arm": "1.104.1", + "sass-embedded-linux-arm64": "1.104.1", + "sass-embedded-linux-musl-arm": "1.104.1", + "sass-embedded-linux-musl-arm64": "1.104.1", + "sass-embedded-linux-musl-riscv64": "1.104.1", + "sass-embedded-linux-musl-x64": "1.104.1", + "sass-embedded-linux-riscv64": "1.104.1", + "sass-embedded-linux-x64": "1.104.1", + "sass-embedded-unknown-all": "1.104.1", + "sass-embedded-win32-arm64": "1.104.1", + "sass-embedded-win32-x64": "1.104.1" }, "dependencies": { "@bufbuild/protobuf": "^2.5.0", - "colorjs.io": "^0.5.0", - "immutable": "^5.0.2", + "colorjs.io": "^0.7.0", + "immutable": "^5.1.5", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", @@ -73,16 +73,17 @@ "@bufbuild/buf": "^1.54.0", "@bufbuild/protoc-gen-es": "^2.5.0", "@eslint/eslintrc": "^3.3.3", - "@eslint/js": "^9.39.2", + "@eslint/js": "^10.0.1", "@types/buffer-builder": "^0.2.0", "@types/google-protobuf": "^3.7.2", "@types/jest": "^30.0.0", - "@types/node": "^25.0.3", + "@types/node": "^26.1.1", "@types/shelljs": "^0.10.0", "@types/supports-color": "^8.1.1", - "@types/tar": "^6.1.0", + "@types/tar": "^7.0.87", "@types/varint": "^6.0.1", "@types/yargs": "^17.0.4", + "@typescript/native": "npm:typescript@^7.0.2", "extract-zip": "^2.0.1", "gts": "^7.0.0", "jest": "^30.0.3", @@ -91,9 +92,9 @@ "simple-git": "^3.15.1", "source-map-js": "^1.0.2", "tar": "^7.4.3", - "ts-jest": "^29.0.5", + "ts-jest": "^29.4.11", "ts-node": "^10.2.1", - "typescript": "^5.0.2", + "typescript": "npm:@typescript/typescript6@^6.0.2", "yaml": "^2.2.1", "yargs": "^17.2.1" } diff --git a/tool/prepare-optional-release.ts b/tool/prepare-optional-release.ts index 9b7d695e..299568e1 100644 --- a/tool/prepare-optional-release.ts +++ b/tool/prepare-optional-release.ts @@ -8,11 +8,7 @@ import * as pkg from '../package.json'; import * as utils from './utils'; export type DartPlatform = - | 'android' - | 'linux' - | 'linux-musl' - | 'macos' - | 'windows'; + 'android' | 'linux' | 'linux-musl' | 'macos' | 'windows'; export type DartArch = 'x64' | 'arm' | 'arm64' | 'riscv64'; const argv = yargs(process.argv.slice(2)) diff --git a/tsconfig.json b/tsconfig.json index 91520088..62d6d6bf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,9 @@ "rootDir": ".", "useUnknownInCatchVariables": false, "declarationDir": "_types", - "lib": ["DOM"] + "lib": ["DOM"], + "types": ["node", "jest"], + "moduleResolution": "bundler", }, "include": [ "package.json",