diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..3b5b9123 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,14 @@ +{ + "extends": "./node_modules/gts/", + "rules": { + "@typescript-eslint/explicit-function-return-type": [ + "error", + {"allowExpressions": true} + ], + "func-style": ["error", "declaration"], + "prefer-const": ["error", {"destructuring": "all"}], + // It would be nice to sort import declaration order as well, but that's not + // autofixable and it's not worth the effort of handling manually. + "sort-imports": ["error", {"ignoreDeclarationSort": true}], + } +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d4323578..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "./node_modules/gts/", - "rules": { - "prefer-const": ["error", {"destructuring": "all"}] - } -} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index aff82a10..782a0ad7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,7 @@ updates: directory: "/" schedule: interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d75baccf..dd3614a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 'lts/*' check-latest: true @@ -46,8 +46,8 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} check-latest: true @@ -89,10 +89,10 @@ jobs: node_version: lts/-2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dart-lang/setup-dart@v1 with: {sdk: stable} - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: {node-version: "${{ matrix.node_version }}"} - name: Check out Dart Sass @@ -140,8 +140,8 @@ jobs: needs: [static_analysis, tests, sass_spec] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 'lts/*' check-latest: true diff --git a/CHANGELOG.md b/CHANGELOG.md index d06b712f..8533ae1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +## 1.77.8 + +* No user-visible changes. + +## 1.77.7 + +* Declarations that appear after nested rules are deprecated, because the + semantics Sass has historically used are different from the semantics + specified by CSS. In the future, Sass will adopt the standard CSS semantics. + + See [the Sass website](https://sass-lang.com/d/mixed-decls) for details. + +* **Potentially breaking bug fix:** `//` in certain places such as unknown + at-rule values was being preserved in the CSS output, leading to potentially + invalid CSS. It's now properly parsed as a silent comment and omitted from the + CSS output. + +## 1.77.6 + +* Fix a few cases where comments and occasionally even whitespace wasn't allowed + between the end of Sass statements and the following semicolon. + +## 1.77.5 + +* Fully trim redundant selectors generated by `@extend`. + +## 1.77.4 + +### Embedded Sass + +* Support passing `Version` input for `fatalDeprecations` as string over + embedded protocol. + +* Fix a bug in the JS Embedded Host where `Version` could be incorrectly accepted + as input for `silenceDeprecations` and `futureDeprecations` in pure JS. + ## 1.77.3 ### Dart API diff --git a/bin/sass.ts b/bin/sass.ts new file mode 100755 index 00000000..d25d834a --- /dev/null +++ b/bin/sass.ts @@ -0,0 +1,25 @@ +#!/usr/bin/env node + +import * as child_process from 'child_process'; +import {compilerCommand} from '../lib/src/compiler-path'; + +// TODO npm/cmd-shim#152 and yarnpkg/berry#6422 - If and when the package +// managers support it, we should make this a proper shell script rather than a +// JS wrapper. + +try { + child_process.execFileSync( + compilerCommand[0], + [...compilerCommand.slice(1), ...process.argv.slice(2)], + { + stdio: 'inherit', + windowsHide: true, + } + ); +} catch (error) { + if (error.code) { + throw error; + } else { + process.exitCode = error.status; + } +} diff --git a/lib/src/compiler-path.ts b/lib/src/compiler-path.ts index 60675e5b..0097ac28 100644 --- a/lib/src/compiler-path.ts +++ b/lib/src/compiler-path.ts @@ -4,20 +4,29 @@ import * as fs from 'fs'; import * as p from 'path'; +import {getElfInterpreter} from './elf'; import {isErrnoException} from './utils'; /** - * Detect if the current running node binary is linked with musl libc by - * checking if the binary contains a string like "/.../ld-musl-$ARCH.so" + * Detect if the given binary is linked with musl libc by checking if + * the interpreter basename starts with "ld-musl-" */ -const isLinuxMusl = function () { - return fs.readFileSync(process.execPath).includes('/ld-musl-'); -}; +function isLinuxMusl(path: string): boolean { + try { + const interpreter = getElfInterpreter(path); + return p.basename(interpreter).startsWith('ld-musl-'); + } catch (error) { + console.warn( + `Warning: Failed to detect linux-musl, fallback to linux-gnu: ${error.message}` + ); + return false; + } +} /** The full command for the embedded compiler executable. */ export const compilerCommand = (() => { const platform = - process.platform === 'linux' && isLinuxMusl() + process.platform === 'linux' && isLinuxMusl(process.execPath) ? 'linux-musl' : (process.platform as string); diff --git a/lib/src/compiler.test.ts b/lib/src/compiler.test.ts index 58fbc61f..df72f27e 100644 --- a/lib/src/compiler.test.ts +++ b/lib/src/compiler.test.ts @@ -9,7 +9,7 @@ import * as compilerModule from './compiler/utils'; import {Compiler, initCompiler} from './compiler/sync'; const createDispatcher = jest.spyOn(compilerModule, 'createDispatcher'); -function getIdHistory() { +function getIdHistory(): number[] { return createDispatcher.mock.calls.map(([id]) => id); } diff --git a/lib/src/compiler/utils.ts b/lib/src/compiler/utils.ts index 09fa031d..80e1e0fc 100644 --- a/lib/src/compiler/utils.ts +++ b/lib/src/compiler/utils.ts @@ -4,7 +4,7 @@ import * as p from 'path'; import * as supportsColor from 'supports-color'; -import {deprecations, getDeprecationIds, Deprecation} from '../deprecations'; +import {Deprecation, deprecations, getDeprecationIds} from '../deprecations'; import {deprotofySourceSpan} from '../deprotofy-span'; import {Dispatcher, DispatcherHandlers} from '../dispatcher'; import {Exception} from '../exception'; diff --git a/lib/src/deprecations.ts b/lib/src/deprecations.ts index e30527c4..26341d82 100644 --- a/lib/src/deprecations.ts +++ b/lib/src/deprecations.ts @@ -2,26 +2,12 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {deprecations} from './vendor/deprecations'; -import {Deprecation, DeprecationOrId} from './vendor/sass'; +import {DeprecationOrId} from './vendor/sass'; import {Version} from './version'; export {deprecations} from './vendor/deprecations'; export {Deprecation, DeprecationOrId, DeprecationStatus} from './vendor/sass'; -/** - * Returns whether the given deprecation was active in the given version. - */ -function isActiveIn(deprecation: Deprecation, version: Version) { - const deprecatedIn = deprecation.deprecatedIn; - if (deprecation.status !== 'active' || !deprecatedIn) return false; - if (version.major > deprecatedIn.major) return true; - if (version.major < deprecatedIn.major) return false; - if (version.minor > deprecatedIn.minor) return true; - if (version.minor < deprecatedIn.minor) return false; - return version.patch >= deprecatedIn.patch; -} - /** * Converts a mixed array of deprecations, IDs, and versions to an array of IDs * that's ready to include in a CompileRequest. @@ -31,9 +17,7 @@ export function getDeprecationIds( ): string[] { return arr.flatMap(item => { if (item instanceof Version) { - return Object.values(deprecations) - .filter(deprecation => isActiveIn(deprecation, item)) - .map(deprecation => deprecation.id); + return arr.map(item => item.toString()); } else if (typeof item === 'string') { return item; } diff --git a/lib/src/dispatcher.ts b/lib/src/dispatcher.ts index a99dee96..48cafefb 100644 --- a/lib/src/dispatcher.ts +++ b/lib/src/dispatcher.ts @@ -8,7 +8,7 @@ import {filter, map, mergeMap, takeUntil} from 'rxjs/operators'; import {OutboundResponse} from './messages'; import * as proto from './vendor/embedded_sass_pb'; import {RequestTracker} from './request-tracker'; -import {PromiseOr, compilerError, thenOr, hostError} from './utils'; +import {PromiseOr, compilerError, hostError, thenOr} from './utils'; // A callback that accepts a response or error. type ResponseCallback = ( diff --git a/lib/src/elf.ts b/lib/src/elf.ts new file mode 100644 index 00000000..1cd396a6 --- /dev/null +++ b/lib/src/elf.ts @@ -0,0 +1,102 @@ +// Copyright 2024 Google LLC. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import * as fs from 'fs'; + +/** Read a chunk of data from a file descriptor into a new Buffer. */ +function readFileDescriptor( + fd: number, + position: number, + length: number +): Buffer { + const buffer = Buffer.alloc(length); + let offset = 0; + while (offset < length) { + const bytesRead = fs.readSync(fd, buffer, { + offset: offset, + position: position + offset, + }); + if (bytesRead === 0) { + throw new Error(`failed to read fd ${fd}`); + } + + offset += bytesRead; + } + return buffer; +} + +/** Parse an ELF file and return its interpreter. */ +export function getElfInterpreter(path: string): string { + const fd = fs.openSync(path, 'r'); + try { + const elfIdentification = new DataView( + readFileDescriptor(fd, 0, 64).buffer + ); + + if ( + elfIdentification.getUint8(0) !== 0x7f || + elfIdentification.getUint8(1) !== 0x45 || + elfIdentification.getUint8(2) !== 0x4c || + elfIdentification.getUint8(3) !== 0x46 + ) { + throw new Error(`${path} is not an ELF file.`); + } + + const elfIdentificationClass = elfIdentification.getUint8(4); + if (elfIdentificationClass !== 1 && elfIdentificationClass !== 2) { + throw new Error(`${path} has an invalid ELF class.`); + } + const elfClass32 = elfIdentificationClass === 1; + + const elfIdentificationData = elfIdentification.getUint8(5); + if (elfIdentificationData !== 1 && elfIdentificationData !== 2) { + throw new Error(`${path} has an invalid endianness.`); + } + const littleEndian = elfIdentificationData === 1; + + // Converting BigUint64 to Number because node Buffer length has to be + // number type, and we don't expect any elf we check with this method to + // be larger than 9007199254740991 bytes. + const programHeadersOffset = elfClass32 + ? elfIdentification.getUint32(28, littleEndian) + : Number(elfIdentification.getBigUint64(32, littleEndian)); + const programHeadersEntrySize = elfClass32 + ? elfIdentification.getUint16(42, littleEndian) + : elfIdentification.getUint16(54, littleEndian); + const programHeadersEntryCount = elfClass32 + ? elfIdentification.getUint16(44, littleEndian) + : elfIdentification.getUint16(56, littleEndian); + + const programHeaders = new DataView( + readFileDescriptor( + fd, + programHeadersOffset, + programHeadersEntrySize * programHeadersEntryCount + ).buffer + ); + for (let i = 0; i < programHeadersEntryCount; i++) { + const byteOffset = i * programHeadersEntrySize; + const segmentType = programHeaders.getUint32(byteOffset, littleEndian); + if (segmentType !== 3) continue; // 3 is PT_INTERP, the interpreter + + const segmentOffset = elfClass32 + ? programHeaders.getUint32(byteOffset + 4, littleEndian) + : Number(programHeaders.getBigUint64(byteOffset + 8, littleEndian)); + const segmentFileSize = elfClass32 + ? programHeaders.getUint32(byteOffset + 16, littleEndian) + : Number(programHeaders.getBigUint64(byteOffset + 32, littleEndian)); + + const buffer = readFileDescriptor(fd, segmentOffset, segmentFileSize); + if (buffer[segmentFileSize - 1] !== 0) { + throw new Error(`${path} is corrupted.`); + } + + return buffer.toString('utf8', 0, segmentFileSize - 1); + } + + throw new Error(`${path} does not contain an interpreter entry.`); + } finally { + fs.closeSync(fd); + } +} diff --git a/lib/src/exception.ts b/lib/src/exception.ts index 48eb6dfd..3e00f40e 100644 --- a/lib/src/exception.ts +++ b/lib/src/exception.ts @@ -19,7 +19,7 @@ export class Exception extends Error implements SassException { this.span = deprotofySourceSpan(failure.span!); } - toString() { + toString(): string { return this.message; } } diff --git a/lib/src/importer-registry.ts b/lib/src/importer-registry.ts index 749a912b..691b4af2 100644 --- a/lib/src/importer-registry.ts +++ b/lib/src/importer-registry.ts @@ -11,7 +11,7 @@ import {CanonicalizeContext} from './canonicalize-context'; import * as utils from './utils'; import {FileImporter, Importer, Options} from './vendor/sass'; import * as proto from './vendor/embedded_sass_pb'; -import {catchOr, thenOr, PromiseOr} from './utils'; +import {PromiseOr, catchOr, thenOr} from './utils'; const entryPointDirectoryKey = Symbol(); diff --git a/lib/src/legacy/importer.ts b/lib/src/legacy/importer.ts index 5977a3d5..68b79e11 100644 --- a/lib/src/legacy/importer.ts +++ b/lib/src/legacy/importer.ts @@ -9,11 +9,11 @@ import * as util from 'util'; import {resolvePath} from './resolve-path'; import { + PromiseOr, + SyncBoolean, fileUrlToPathCrossPlatform, isErrnoException, thenOr, - PromiseOr, - SyncBoolean, } from '../utils'; import { Importer, @@ -26,10 +26,10 @@ import { LegacySyncImporter, } from '../vendor/sass'; import { - pathToLegacyFileUrl, legacyFileUrlToPath, legacyImporterProtocol, legacyImporterProtocolPrefix, + pathToLegacyFileUrl, } from './utils'; /** diff --git a/lib/src/legacy/index.ts b/lib/src/legacy/index.ts index d7543e35..f5467565 100644 --- a/lib/src/legacy/index.ts +++ b/lib/src/legacy/index.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import * as p from 'path'; -import {pathToFileURL, URL} from 'url'; +import {URL, pathToFileURL} from 'url'; import {NodePackageImporter} from '../importer-registry'; import {Exception} from '../exception'; @@ -15,11 +15,11 @@ import { compileStringAsync, } from '../compile'; import { + SyncBoolean, fileUrlToPathCrossPlatform, isNullOrUndefined, pathToUrlString, withoutExtension, - SyncBoolean, } from '../utils'; import { CompileResult, @@ -32,11 +32,9 @@ import { LegacyStringOptions, Options, StringOptions, - Importer, - FileImporter, } from '../vendor/sass'; import {wrapFunction} from './value/wrap'; -import {endOfLoadProtocol, LegacyImporterWrapper} from './importer'; +import {LegacyImporterWrapper, endOfLoadProtocol} from './importer'; import { legacyImporterProtocol, pathToLegacyFileUrl, diff --git a/lib/src/legacy/value/wrap.ts b/lib/src/legacy/value/wrap.ts index 178da2ec..7da2e1cd 100644 --- a/lib/src/legacy/value/wrap.ts +++ b/lib/src/legacy/value/wrap.ts @@ -42,7 +42,7 @@ export function wrapFunction( } else { return args => new Promise((resolve, reject) => { - const done = (result: unknown) => { + function done(result: unknown): void { try { if (result instanceof Error) { reject(result); @@ -52,7 +52,7 @@ export function wrapFunction( } catch (error: unknown) { reject(error); } - }; + } // The cast here is necesary to work around microsoft/TypeScript#33815. const syncResult = (callback as (...args: unknown[]) => unknown).apply( diff --git a/lib/src/message-transformer.test.ts b/lib/src/message-transformer.test.ts index d74f2e28..767999c9 100644 --- a/lib/src/message-transformer.test.ts +++ b/lib/src/message-transformer.test.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {Subject, Observable} from 'rxjs'; +import {Observable, Subject} from 'rxjs'; import * as varint from 'varint'; import {expectObservableToError} from '../../test/utils'; diff --git a/lib/src/packet-transformer.test.ts b/lib/src/packet-transformer.test.ts index 4533f002..78297883 100644 --- a/lib/src/packet-transformer.test.ts +++ b/lib/src/packet-transformer.test.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {Subject, Observable} from 'rxjs'; +import {Observable, Subject} from 'rxjs'; import {PacketTransformer} from './packet-transformer'; @@ -55,7 +55,7 @@ describe('packet transformer', () => { describe('decode', () => { let rawBuffers$: Subject; - function expectDecoding(expected: Buffer[], done: () => void) { + function expectDecoding(expected: Buffer[], done: () => void): void { const actual: Buffer[] = []; packets.outboundProtobufs$.subscribe({ next: protobuf => actual.push(protobuf), diff --git a/lib/src/protofier.ts b/lib/src/protofier.ts index 8b3acd7c..c9c28e39 100644 --- a/lib/src/protofier.ts +++ b/lib/src/protofier.ts @@ -10,19 +10,19 @@ import {FunctionRegistry} from './function-registry'; import {SassArgumentList} from './value/argument-list'; import {SassColor, KnownColorSpace} from './value/color'; import {SassFunction} from './value/function'; -import {SassList, ListSeparator} from './value/list'; +import {ListSeparator, SassList} from './value/list'; import {SassMap} from './value/map'; import {SassNumber} from './value/number'; import {SassString} from './value/string'; import {Value} from './value'; import {sassNull} from './value/null'; -import {sassTrue, sassFalse} from './value/boolean'; +import {sassFalse, sassTrue} from './value/boolean'; import { - CalculationValue, - SassCalculation, CalculationInterpolation, CalculationOperation, CalculationOperator, + CalculationValue, + SassCalculation, } from './value/calculations'; import {SassMixin} from './value/mixin'; diff --git a/lib/src/request-tracker.ts b/lib/src/request-tracker.ts index f26db4ca..200f51c1 100644 --- a/lib/src/request-tracker.ts +++ b/lib/src/request-tracker.ts @@ -16,7 +16,7 @@ export class RequestTracker { > = []; /** The next available request ID. */ - get nextId() { + get nextId(): number { for (let i = 0; i < this.requests.length; i++) { if (this.requests[i] === undefined || this.requests[i] === null) { return i; @@ -32,7 +32,7 @@ export class RequestTracker { add( id: number, expectedResponseType: InboundResponseType | OutboundResponseType - ) { + ): void { if (id < 0) { throw Error(`Invalid request ID ${id}.`); } else if (this.requests[id]) { @@ -47,7 +47,7 @@ export class RequestTracker { * Resolves a pending request with matching ID `id` and expected response type * `type`. Throws an error if the Protocol Error is violated. */ - resolve(id: number, type: InboundResponseType | OutboundResponseType) { + resolve(id: number, type: InboundResponseType | OutboundResponseType): void { if (this.requests[id] === undefined || this.requests[id] === null) { throw Error(`Response ID ${id} does not match any pending requests.`); } else if (this.requests[id] !== type) { diff --git a/lib/src/sync-process/sync-message-port.ts b/lib/src/sync-process/sync-message-port.ts index 6c4a8751..7c82a343 100644 --- a/lib/src/sync-process/sync-message-port.ts +++ b/lib/src/sync-process/sync-message-port.ts @@ -5,10 +5,10 @@ import {strict as assert} from 'assert'; import {EventEmitter} from 'events'; import { - receiveMessageOnPort, MessageChannel, MessagePort, TransferListItem, + receiveMessageOnPort, } from 'worker_threads'; // TODO(nex3): Make this its own package. diff --git a/lib/src/sync-process/worker.ts b/lib/src/sync-process/worker.ts index 2b6e4c13..89542961 100644 --- a/lib/src/sync-process/worker.ts +++ b/lib/src/sync-process/worker.ts @@ -3,12 +3,12 @@ // https://opensource.org/licenses/MIT. import { - parentPort, - workerData, MessagePort, TransferListItem, + parentPort, + workerData, } from 'worker_threads'; -import {spawn, SpawnOptionsWithoutStdio} from 'child_process'; +import {SpawnOptionsWithoutStdio, spawn} from 'child_process'; import {strict as assert} from 'assert'; import {SyncMessagePort} from './sync-message-port'; diff --git a/lib/src/value/argument-list.ts b/lib/src/value/argument-list.ts index b898796a..029e2658 100644 --- a/lib/src/value/argument-list.ts +++ b/lib/src/value/argument-list.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {isOrderedMap, List, OrderedMap} from 'immutable'; +import {List, OrderedMap, isOrderedMap} from 'immutable'; import {ListSeparator, SassList} from './list'; import {Value} from './index'; diff --git a/lib/src/value/calculations.ts b/lib/src/value/calculations.ts index ce32f610..7f4c2a4e 100644 --- a/lib/src/value/calculations.ts +++ b/lib/src/value/calculations.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {hash, List, ValueObject} from 'immutable'; +import {List, ValueObject, hash} from 'immutable'; import {Value} from './index'; import {SassNumber} from './number'; @@ -23,9 +23,12 @@ function assertCalculationValue(value: CalculationValue): void { } } -const isValidClampArg = (value: CalculationValue): boolean => - value instanceof CalculationInterpolation || - (value instanceof SassString && !value.hasQuotes); +function isValidClampArg(value: CalculationValue): boolean { + return ( + value instanceof CalculationInterpolation || + (value instanceof SassString && !value.hasQuotes) + ); +} /* A SassScript calculation */ export class SassCalculation extends Value { diff --git a/lib/src/value/list.ts b/lib/src/value/list.ts index 97ec57d8..d8175bb0 100644 --- a/lib/src/value/list.ts +++ b/lib/src/value/list.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {hash, isList, List} from 'immutable'; +import {List, hash, isList} from 'immutable'; import {Value} from './index'; import {SassMap} from './map'; diff --git a/lib/src/value/number.ts b/lib/src/value/number.ts index 9bf26cc8..11d69e56 100644 --- a/lib/src/value/number.ts +++ b/lib/src/value/number.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {hash, List} from 'immutable'; +import {List, hash} from 'immutable'; import {asImmutableList, valueError} from '../utils'; import {Value} from './index'; diff --git a/lib/src/value/utils.ts b/lib/src/value/utils.ts index 18584bfb..d40ed98c 100644 --- a/lib/src/value/utils.ts +++ b/lib/src/value/utils.ts @@ -116,7 +116,7 @@ export function fuzzyAssertInRange( } /** Returns `dividend % modulus`, but always in the range `[0, modulus)`. */ -export function positiveMod(dividend: number, modulus: number) { +export function positiveMod(dividend: number, modulus: number): number { const result = dividend % modulus; return result < 0 ? result + modulus : result; } diff --git a/lib/src/version.ts b/lib/src/version.ts index 772b8258..79f61371 100644 --- a/lib/src/version.ts +++ b/lib/src/version.ts @@ -21,4 +21,7 @@ export class Version implements api.Version { parseInt(match[3]) ); } + toString(): string { + return `${this.major}.${this.minor}.${this.patch}`; + } } diff --git a/npm/android-arm/package.json b/npm/android-arm/package.json index 31001aef..8d467a14 100644 --- a/npm/android-arm/package.json +++ b/npm/android-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm", - "version": "1.77.3", + "version": "1.77.8", "description": "The android-arm binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "android" ], diff --git a/npm/android-arm64/package.json b/npm/android-arm64/package.json index 849f79d7..6648630f 100644 --- a/npm/android-arm64/package.json +++ b/npm/android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm64", - "version": "1.77.3", + "version": "1.77.8", "description": "The android-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "android" ], diff --git a/npm/android-ia32/package.json b/npm/android-ia32/package.json index beeeba0d..4d9c9bc4 100644 --- a/npm/android-ia32/package.json +++ b/npm/android-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-ia32", - "version": "1.77.3", + "version": "1.77.8", "description": "The android-ia32 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "android" ], diff --git a/npm/android-riscv64/README.md b/npm/android-riscv64/README.md new file mode 100644 index 00000000..5c79fe81 --- /dev/null +++ b/npm/android-riscv64/README.md @@ -0,0 +1,3 @@ +# `sass-embedded-android-riscv64` + +This is the **android-riscv64** binary for [`sass-embedded`](https://www.npmjs.com/package/sass-embedded) diff --git a/npm/android-riscv64/package.json b/npm/android-riscv64/package.json new file mode 100644 index 00000000..7096a34d --- /dev/null +++ b/npm/android-riscv64/package.json @@ -0,0 +1,20 @@ +{ + "name": "sass-embedded-android-riscv64", + "version": "1.77.8", + "description": "The android-riscv64 binary for sass-embedded", + "repository": "sass/embedded-host-node", + "author": "Google Inc.", + "license": "MIT", + "files": [ + "dart-sass/**/*" + ], + "engines": { + "node": ">=14.0.0" + }, + "os": [ + "android" + ], + "cpu": [ + "riscv64" + ] +} diff --git a/npm/android-x64/package.json b/npm/android-x64/package.json index 825f950c..598f2308 100644 --- a/npm/android-x64/package.json +++ b/npm/android-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-x64", - "version": "1.77.3", + "version": "1.77.8", "description": "The android-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "android" ], diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json index fcfc9f1d..08a81de6 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-arm64", - "version": "1.77.3", + "version": "1.77.8", "description": "The darwin-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "darwin" ], diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json index a3b9b1e3..e3428f50 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-x64", - "version": "1.77.3", + "version": "1.77.8", "description": "The darwin-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "darwin" ], diff --git a/npm/linux-arm/package.json b/npm/linux-arm/package.json index 45f56274..dd910307 100644 --- a/npm/linux-arm/package.json +++ b/npm/linux-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm", - "version": "1.77.3", + "version": "1.77.8", "description": "The linux-arm binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "linux" ], diff --git a/npm/linux-arm64/package.json b/npm/linux-arm64/package.json index 61d82dd5..9cd90d34 100644 --- a/npm/linux-arm64/package.json +++ b/npm/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm64", - "version": "1.77.3", + "version": "1.77.8", "description": "The linux-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "linux" ], diff --git a/npm/linux-ia32/package.json b/npm/linux-ia32/package.json index 82a27457..48356826 100644 --- a/npm/linux-ia32/package.json +++ b/npm/linux-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-ia32", - "version": "1.77.3", + "version": "1.77.8", "description": "The linux-ia32 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "linux" ], diff --git a/npm/linux-musl-arm/package.json b/npm/linux-musl-arm/package.json index 0ed6b12c..24ace4f8 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.77.3", + "version": "1.77.8", "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 c7192ad7..a1ffe55e 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.77.3", + "version": "1.77.8", "description": "The linux-musl-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-ia32/package.json b/npm/linux-musl-ia32/package.json index a6726169..60746951 100644 --- a/npm/linux-musl-ia32/package.json +++ b/npm/linux-musl-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-musl-ia32", - "version": "1.77.3", + "version": "1.77.8", "description": "The linux-musl-ia32 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-musl-riscv64/README.md b/npm/linux-musl-riscv64/README.md new file mode 100644 index 00000000..b66e939f --- /dev/null +++ b/npm/linux-musl-riscv64/README.md @@ -0,0 +1,3 @@ +# `sass-embedded-linux-musl-riscv64` + +This is the **linux-musl-riscv64** binary for [`sass-embedded`](https://www.npmjs.com/package/sass-embedded) diff --git a/npm/linux-musl-riscv64/package.json b/npm/linux-musl-riscv64/package.json new file mode 100644 index 00000000..c4cd9bbc --- /dev/null +++ b/npm/linux-musl-riscv64/package.json @@ -0,0 +1,20 @@ +{ + "name": "sass-embedded-linux-musl-riscv64", + "version": "1.77.8", + "description": "The linux-musl-riscv64 binary for sass-embedded", + "repository": "sass/embedded-host-node", + "author": "Google Inc.", + "license": "MIT", + "files": [ + "dart-sass/**/*" + ], + "engines": { + "node": ">=14.0.0" + }, + "os": [ + "linux" + ], + "cpu": [ + "riscv64" + ] +} diff --git a/npm/linux-musl-x64/package.json b/npm/linux-musl-x64/package.json index 0d8d06d8..ac4afa94 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.77.3", + "version": "1.77.8", "description": "The linux-musl-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-riscv64/README.md b/npm/linux-riscv64/README.md new file mode 100644 index 00000000..70bf2c32 --- /dev/null +++ b/npm/linux-riscv64/README.md @@ -0,0 +1,3 @@ +# `sass-embedded-linux-riscv64` + +This is the **linux-riscv64** binary for [`sass-embedded`](https://www.npmjs.com/package/sass-embedded) diff --git a/npm/linux-riscv64/package.json b/npm/linux-riscv64/package.json new file mode 100644 index 00000000..332e08b5 --- /dev/null +++ b/npm/linux-riscv64/package.json @@ -0,0 +1,20 @@ +{ + "name": "sass-embedded-linux-riscv64", + "version": "1.77.8", + "description": "The linux-riscv64 binary for sass-embedded", + "repository": "sass/embedded-host-node", + "author": "Google Inc.", + "license": "MIT", + "files": [ + "dart-sass/**/*" + ], + "engines": { + "node": ">=14.0.0" + }, + "os": [ + "linux" + ], + "cpu": [ + "riscv64" + ] +} diff --git a/npm/linux-x64/package.json b/npm/linux-x64/package.json index 5db3156b..308a38fb 100644 --- a/npm/linux-x64/package.json +++ b/npm/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-x64", - "version": "1.77.3", + "version": "1.77.8", "description": "The linux-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass" - }, "os": [ "linux" ], diff --git a/npm/win32-arm64/package.json b/npm/win32-arm64/package.json index 9f6ee597..27a53b3b 100644 --- a/npm/win32-arm64/package.json +++ b/npm/win32-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-arm64", - "version": "1.77.3", + "version": "1.77.8", "description": "The win32-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass.bat" - }, "os": [ "win32" ], diff --git a/npm/win32-ia32/package.json b/npm/win32-ia32/package.json index 40fa2ee2..97b1df67 100644 --- a/npm/win32-ia32/package.json +++ b/npm/win32-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-ia32", - "version": "1.77.3", + "version": "1.77.8", "description": "The win32-ia32 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass.bat" - }, "os": [ "win32" ], diff --git a/npm/win32-x64/package.json b/npm/win32-x64/package.json index 21561855..5ecbe218 100644 --- a/npm/win32-x64/package.json +++ b/npm/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-x64", - "version": "1.77.3", + "version": "1.77.8", "description": "The win32-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -11,9 +11,6 @@ "engines": { "node": ">=14.0.0" }, - "bin": { - "sass": "./dart-sass/sass.bat" - }, "os": [ "win32" ], diff --git a/package.json b/package.json index 8295fa64..07dae3eb 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "sass-embedded", - "version": "1.77.3", + "version": "1.77.8", "protocol-version": "3.0.0-dev", - "compiler-version": "1.77.3", + "compiler-version": "1.77.8", "description": "Node.js library that communicates with Embedded Dart Sass using the Embedded Sass protocol", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -23,6 +23,7 @@ "engines": { "node": ">=16.0.0" }, + "bin": {"sass": "dist/bin/sass.js"}, "scripts": { "init": "ts-node ./tool/init.ts", "check": "npm-run-all check:gts check:tsc", @@ -35,23 +36,26 @@ "test": "jest" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.77.3", - "sass-embedded-android-arm64": "1.77.3", - "sass-embedded-android-ia32": "1.77.3", - "sass-embedded-android-x64": "1.77.3", - "sass-embedded-darwin-arm64": "1.77.3", - "sass-embedded-darwin-x64": "1.77.3", - "sass-embedded-linux-arm": "1.77.3", - "sass-embedded-linux-arm64": "1.77.3", - "sass-embedded-linux-ia32": "1.77.3", - "sass-embedded-linux-x64": "1.77.3", - "sass-embedded-linux-musl-arm": "1.77.3", - "sass-embedded-linux-musl-arm64": "1.77.3", - "sass-embedded-linux-musl-ia32": "1.77.3", - "sass-embedded-linux-musl-x64": "1.77.3", - "sass-embedded-win32-arm64": "1.77.3", - "sass-embedded-win32-ia32": "1.77.3", - "sass-embedded-win32-x64": "1.77.3" + "sass-embedded-android-arm": "1.77.8", + "sass-embedded-android-arm64": "1.77.8", + "sass-embedded-android-ia32": "1.77.8", + "sass-embedded-android-riscv64": "1.77.8", + "sass-embedded-android-x64": "1.77.8", + "sass-embedded-darwin-arm64": "1.77.8", + "sass-embedded-darwin-x64": "1.77.8", + "sass-embedded-linux-arm": "1.77.8", + "sass-embedded-linux-arm64": "1.77.8", + "sass-embedded-linux-ia32": "1.77.8", + "sass-embedded-linux-riscv64": "1.77.8", + "sass-embedded-linux-x64": "1.77.8", + "sass-embedded-linux-musl-arm": "1.77.8", + "sass-embedded-linux-musl-arm64": "1.77.8", + "sass-embedded-linux-musl-ia32": "1.77.8", + "sass-embedded-linux-musl-riscv64": "1.77.8", + "sass-embedded-linux-musl-x64": "1.77.8", + "sass-embedded-win32-arm64": "1.77.8", + "sass-embedded-win32-ia32": "1.77.8", + "sass-embedded-win32-x64": "1.77.8" }, "dependencies": { "@bufbuild/protobuf": "^1.0.0", @@ -68,7 +72,7 @@ "@types/buffer-builder": "^0.2.0", "@types/google-protobuf": "^3.7.2", "@types/jest": "^29.4.0", - "@types/node": "^20.1.0", + "@types/node": "^22.0.0", "@types/shelljs": "^0.8.8", "@types/supports-color": "^8.1.1", "@types/tar": "^6.1.0", diff --git a/test/utils.ts b/test/utils.ts index f8733917..c071d481 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -48,7 +48,7 @@ export function expectEqualIgnoringWhitespace( string1: string, string2: string ): void { - function strip(str: string) { + function strip(str: string): string { return str.replace(/\s+/g, ''); } expect(strip(string1)).toBe(strip(string2)); diff --git a/tool/get-deprecations.ts b/tool/get-deprecations.ts index 73618b4d..9571335f 100644 --- a/tool/get-deprecations.ts +++ b/tool/get-deprecations.ts @@ -34,7 +34,7 @@ function toVersionCode(version: string | undefined): string { * Generates the list of deprecations based on the YAML file in the language * repo. */ -export async function getDeprecations(outDirectory: string) { +export async function getDeprecations(outDirectory: string): Promise { const yamlText = fs.readFileSync(yamlFile, 'utf8'); const deprecations = parse(yamlText) as YamlData; diff --git a/tool/prepare-optional-release.ts b/tool/prepare-optional-release.ts index c0e606c3..29f488e8 100644 --- a/tool/prepare-optional-release.ts +++ b/tool/prepare-optional-release.ts @@ -13,7 +13,7 @@ export type DartPlatform = | 'linux-musl' | 'macos' | 'windows'; -export type DartArch = 'ia32' | 'x64' | 'arm' | 'arm64'; +export type DartArch = 'ia32' | 'x64' | 'arm' | 'arm64' | 'riscv64'; const argv = yargs(process.argv.slice(2)) .option('package', { @@ -61,6 +61,8 @@ export function nodeArchToDartArch(arch: string): DartArch { return 'arm'; case 'arm64': return 'arm64'; + case 'riscv64': + return 'riscv64'; default: throw Error(`Architecture ${arch} is not supported.`); } @@ -112,47 +114,6 @@ async function downloadRelease(options: { await fs.unlink(zippedAssetPath); } -// Patch the launcher script if needed. -// -// For linux both `-linux-` and `-linux-musl-` packages will be installed -// because npm doesn't know how to select packages based on LibC. To avoid -// conflicts, only the `-linux-` packages have "bin" scripts defined in -// package.json, which we patch to detect which LibC is available and launch the -// correct binary. -async function patchLauncherScript( - path: string, - dartPlatform: DartPlatform, - dartArch: DartArch -) { - if (dartPlatform !== 'linux') return; - - const scriptPath = p.join(path, 'dart-sass', 'sass'); - console.log(`Patching ${scriptPath} script.`); - - const shebang = Buffer.from('#!/bin/sh\n'); - const buffer = await fs.readFile(scriptPath); - if (!buffer.subarray(0, shebang.length).equals(shebang)) { - throw new Error(`${scriptPath} is not a shell script!`); - } - - const lines = buffer.toString('utf-8').split('\n'); - const index = lines.findIndex(line => line.startsWith('path=')); - if (index < 0) { - throw new Error(`The format of ${scriptPath} has changed!`); - } - - lines.splice( - index + 1, - 0, - '# Detect linux-musl', - 'if grep -qm 1 /ld-musl- /proc/self/exe; then', - ` path="$path/../../sass-embedded-linux-musl-${dartArch}/dart-sass"`, - 'fi' - ); - - await fs.writeFile(scriptPath, lines.join('\n')); -} - void (async () => { try { const version = pkg['compiler-version'] as string; @@ -178,7 +139,6 @@ void (async () => { `${getArchiveExtension(dartPlatform)}`, outPath, }); - await patchLauncherScript(outPath, dartPlatform, dartArch); } catch (error) { console.error(error); process.exitCode = 1; diff --git a/tool/prepare-release.ts b/tool/prepare-release.ts index d5d3ba0d..4065ffcf 100644 --- a/tool/prepare-release.ts +++ b/tool/prepare-release.ts @@ -6,6 +6,7 @@ import {promises as fs} from 'fs'; import * as shell from 'shelljs'; import * as pkg from '../package.json'; +import {getDeprecations} from './get-deprecations'; import {getLanguageRepo} from './get-language-repo'; void (async () => { @@ -14,6 +15,8 @@ void (async () => { await getLanguageRepo('lib/src/vendor'); + await getDeprecations('lib/src/vendor'); + console.log('Transpiling TS into dist.'); shell.exec('tsc -p tsconfig.build.json'); shell.cp('lib/index.mjs', 'dist/lib/index.mjs'); @@ -32,7 +35,7 @@ void (async () => { // Quick sanity checks to make sure the release we are preparing is a suitable // candidate for release. -async function sanityCheckBeforeRelease() { +async function sanityCheckBeforeRelease(): Promise { console.log('Running sanity checks before releasing.'); const releaseVersion = pkg.version; diff --git a/tool/utils.ts b/tool/utils.ts index 1a9e9ae1..c68d3012 100644 --- a/tool/utils.ts +++ b/tool/utils.ts @@ -2,7 +2,7 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {promises as fs, existsSync, lstatSync} from 'fs'; +import {existsSync, promises as fs, lstatSync} from 'fs'; import * as p from 'path'; import * as shell from 'shelljs'; diff --git a/tsconfig.build.json b/tsconfig.build.json index 0673a92b..5ca9532f 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "exclude": [ + "jest.config.js", "lib/src/vendor/dart-sass/**", "**/*.test.ts" ] diff --git a/tsconfig.json b/tsconfig.json index 35577090..d83f95cd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ }, "include": [ "*.ts", + "bin/*.ts", "lib/**/*.ts", "tool/**/*.ts", "test/**/*.ts"