diff --git a/CHANGELOG.md b/CHANGELOG.md index 1979177f..d06b712f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,51 @@ +## 1.77.3 + +### Dart API + +* `Deprecation.duplicateVariableFlags` has been deprecated and replaced with + `Deprecation.duplicateVarFlags` to make it consistent with the + `duplicate-var-flags` name used on the command line and in the JS API. + +## 1.77.2 + +* Don't emit deprecation warnings for functions and mixins beginning with `__`. + +* Allow user-defined functions whose names begin with `_` and otherwise look + like vendor-prefixed functions with special CSS syntax. + +### Command-Line Interface + +* Properly handle the `--silence-deprecation` flag. + +* Handle the `--fatal-deprecation` and `--future-deprecation` flags for + `--interactive` mode. + +## 1.77.1 + +* Fix a crash that could come up with importers in certain contexts. + +## 1.77.0 + +* *Don't* throw errors for at-rules in keyframe blocks. + +## 1.76.0 + +* Throw errors for misplaced statements in keyframe blocks. + +* Mixins and functions whose names begin with `--` are now deprecated for + forwards-compatibility with the in-progress CSS functions and mixins spec. + This deprecation is named `css-function-mixin`. + +## 1.75.0 + +* Fix a bug in which stylesheet canonicalization could be cached incorrectly + when custom importers or the Node.js package importer made decisions based on + the URL of the containing stylesheet. + +### JS API + +* Allow `importer` to be passed without `url` in `StringOptionsWithImporter`. + ## 1.74.1 * No user-visible changes. diff --git a/lib/index.ts b/lib/index.ts index 96ce209b..e148d962 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -40,8 +40,8 @@ export { Deprecation, DeprecationOrId, DeprecationStatus, - Version, } from './src/deprecations'; +export {Version} from './src/version'; export {render, renderSync} from './src/legacy'; export const info = `sass-embedded\t${pkg.version}`; diff --git a/lib/src/canonicalize-context.ts b/lib/src/canonicalize-context.ts new file mode 100644 index 00000000..46111a8a --- /dev/null +++ b/lib/src/canonicalize-context.ts @@ -0,0 +1,32 @@ +// 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. + +export class CanonicalizeContext { + readonly fromImport: boolean; + + private readonly _containingUrl: URL | null; + + get containingUrl(): URL | null { + this._containingUrlAccessed = true; + return this._containingUrl; + } + + private _containingUrlAccessed = false; + + /** + * Whether the `containingUrl` getter has been accessed. + * + * This is marked as public so that the importer registry can access it, but + * it's not part of the package's public API and should not be accessed by + * user code. It may be renamed or removed without warning in the future. + */ + get containingUrlAccessed(): boolean { + return this._containingUrlAccessed; + } + + constructor(containingUrl: URL | null, fromImport: boolean) { + this._containingUrl = containingUrl; + this.fromImport = fromImport; + } +} diff --git a/lib/src/deprecations.ts b/lib/src/deprecations.ts index f69284cc..e30527c4 100644 --- a/lib/src/deprecations.ts +++ b/lib/src/deprecations.ts @@ -2,33 +2,17 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import * as api from './vendor/sass'; +import {deprecations} from './vendor/deprecations'; +import {Deprecation, DeprecationOrId} from './vendor/sass'; +import {Version} from './version'; +export {deprecations} from './vendor/deprecations'; export {Deprecation, DeprecationOrId, DeprecationStatus} from './vendor/sass'; -export class Version implements api.Version { - constructor( - readonly major: number, - readonly minor: number, - readonly patch: number - ) {} - static parse(version: string): Version { - const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/); - if (match === null) { - throw new Error(`Invalid version ${version}`); - } - return new Version( - parseInt(match[1]), - parseInt(match[2]), - parseInt(match[3]) - ); - } -} - /** * Returns whether the given deprecation was active in the given version. */ -function isActiveIn(deprecation: api.Deprecation, version: 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; @@ -43,7 +27,7 @@ function isActiveIn(deprecation: api.Deprecation, version: Version) { * that's ready to include in a CompileRequest. */ export function getDeprecationIds( - arr: (api.DeprecationOrId | Version)[] + arr: (DeprecationOrId | Version)[] ): string[] { return arr.flatMap(item => { if (item instanceof Version) { @@ -56,132 +40,3 @@ export function getDeprecationIds( return item.id; }); } - -export const deprecations: typeof api.deprecations = { - 'call-string': { - id: 'call-string', - status: 'active', - deprecatedIn: new Version(0, 0, 0), - obsoleteIn: null, - description: 'Passing a string directly to meta.call().', - }, - elseif: { - id: 'elseif', - status: 'active', - deprecatedIn: new Version(1, 3, 2), - obsoleteIn: null, - description: '@elseif.', - }, - 'moz-document': { - id: 'moz-document', - status: 'active', - deprecatedIn: new Version(1, 7, 2), - obsoleteIn: null, - description: '@-moz-document.', - }, - 'relative-canonical': { - id: 'relative-canonical', - status: 'active', - deprecatedIn: new Version(1, 14, 2), - obsoleteIn: null, - }, - 'new-global': { - id: 'new-global', - status: 'active', - deprecatedIn: new Version(1, 17, 2), - obsoleteIn: null, - description: 'Declaring new variables with !global.', - }, - 'color-module-compat': { - id: 'color-module-compat', - status: 'active', - deprecatedIn: new Version(1, 23, 0), - obsoleteIn: null, - description: - 'Using color module functions in place of plain CSS functions.', - }, - 'slash-div': { - id: 'slash-div', - status: 'active', - deprecatedIn: new Version(1, 33, 0), - obsoleteIn: null, - description: '/ operator for division.', - }, - 'bogus-combinators': { - id: 'bogus-combinators', - status: 'active', - deprecatedIn: new Version(1, 54, 0), - obsoleteIn: null, - description: 'Leading, trailing, and repeated combinators.', - }, - 'strict-unary': { - id: 'strict-unary', - status: 'active', - deprecatedIn: new Version(1, 55, 0), - obsoleteIn: null, - description: 'Ambiguous + and - operators.', - }, - 'function-units': { - id: 'function-units', - status: 'active', - deprecatedIn: new Version(1, 56, 0), - obsoleteIn: null, - description: 'Passing invalid units to built-in functions.', - }, - 'duplicate-var-flags': { - id: 'duplicate-var-flags', - status: 'active', - deprecatedIn: new Version(1, 62, 0), - obsoleteIn: null, - description: 'Using !default or !global multiple times for one variable.', - }, - 'null-alpha': { - id: 'null-alpha', - status: 'active', - deprecatedIn: new Version(1, 62, 3), - obsoleteIn: null, - description: 'Passing null as alpha in the JS API.', - }, - 'abs-percent': { - id: 'abs-percent', - status: 'active', - deprecatedIn: new Version(1, 65, 0), - obsoleteIn: null, - description: 'Passing percentages to the Sass abs() function.', - }, - 'fs-importer-cwd': { - id: 'fs-importer-cwd', - status: 'active', - deprecatedIn: new Version(1, 73, 0), - obsoleteIn: null, - description: - 'Using the current working directory as an implicit load path.', - }, - 'color-4-api': { - id: 'color-4-api', - status: 'active', - deprecatedIn: new Version(1, 76, 0), - obsoleteIn: null, - description: 'Methods of interacting with legacy SassColors.', - }, - 'color-functions': { - id: 'color-functions', - status: 'active', - deprecatedIn: new Version(1, 76, 0), - obsoleteIn: null, - description: 'Using global Sass color functions.', - }, - import: { - id: 'import', - status: 'future', - deprecatedIn: null, - obsoleteIn: null, - description: '@import rules.', - }, - 'user-authored': { - id: 'user-authored', - status: 'user', - deprecatedIn: null, - obsoleteIn: null, - }, -}; diff --git a/lib/src/importer-registry.ts b/lib/src/importer-registry.ts index 9a1cb337..749a912b 100644 --- a/lib/src/importer-registry.ts +++ b/lib/src/importer-registry.ts @@ -7,6 +7,7 @@ import * as p from 'path'; import {URL} from 'url'; import {inspect} from 'util'; +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'; @@ -115,21 +116,22 @@ export class ImporterRegistry { throw utils.compilerError('Unknown CanonicalizeRequest.importer_id'); } + const canonicalizeContext = new CanonicalizeContext( + request.containingUrl ? new URL(request.containingUrl) : null, + request.fromImport + ); + return catchOr( () => { return thenOr( - importer.canonicalize(request.url, { - fromImport: request.fromImport, - containingUrl: request.containingUrl - ? new URL(request.containingUrl) - : null, - }), + importer.canonicalize(request.url, canonicalizeContext), url => new proto.InboundMessage_CanonicalizeResponse({ result: url === null ? {case: undefined} : {case: 'url', value: url.toString()}, + containingUrlUnused: !canonicalizeContext.containingUrlAccessed, }) ); }, @@ -197,17 +199,21 @@ export class ImporterRegistry { throw utils.compilerError('Unknown FileImportRequest.importer_id'); } + const canonicalizeContext = new CanonicalizeContext( + request.containingUrl ? new URL(request.containingUrl) : null, + request.fromImport + ); + return catchOr( () => { return thenOr( - importer.findFileUrl(request.url, { - fromImport: request.fromImport, - containingUrl: request.containingUrl - ? new URL(request.containingUrl) - : null, - }), + importer.findFileUrl(request.url, canonicalizeContext), url => { - if (!url) return new proto.InboundMessage_FileImportResponse(); + if (!url) { + return new proto.InboundMessage_FileImportResponse({ + containingUrlUnused: !canonicalizeContext.containingUrlAccessed, + }); + } if (url.protocol !== 'file:') { throw ( `FileImporter ${inspect(importer)} returned non-file: URL ` + @@ -216,6 +222,7 @@ export class ImporterRegistry { } return new proto.InboundMessage_FileImportResponse({ result: {case: 'fileUrl', value: url.toString()}, + containingUrlUnused: !canonicalizeContext.containingUrlAccessed, }); } ); diff --git a/lib/src/legacy/importer.ts b/lib/src/legacy/importer.ts index 97ef9117..5977a3d5 100644 --- a/lib/src/legacy/importer.ts +++ b/lib/src/legacy/importer.ts @@ -86,10 +86,34 @@ export class LegacyImporterWrapper canonicalize( url: string, - options: {fromImport: boolean} + options: {fromImport: boolean; containingUrl: URL | null} ): PromiseOr { if (url.startsWith(endOfLoadProtocol)) return new URL(url); + // Emulate a base importer instead of using a real base importer, + // because we want to mark containingUrl as used, which is impossible + // in a real base importer. + if (options.containingUrl !== null) { + try { + const absoluteUrl = new URL(url, options.containingUrl).toString(); + const resolved = this.canonicalize(absoluteUrl, { + fromImport: options.fromImport, + containingUrl: null, + }); + if (resolved !== null) return resolved; + } catch (error: unknown) { + if ( + error instanceof TypeError && + isErrnoException(error) && + error.code === 'ERR_INVALID_URL' + ) { + // ignore + } else { + throw error; + } + } + } + if ( url.startsWith(legacyImporterProtocolPrefix) || url.startsWith(legacyImporterProtocol) diff --git a/lib/src/legacy/index.ts b/lib/src/legacy/index.ts index 404937cf..d7543e35 100644 --- a/lib/src/legacy/index.ts +++ b/lib/src/legacy/index.ts @@ -184,11 +184,20 @@ function convertStringOptions( ): StringOptions & {legacy: true} { const modernOptions = convertOptions(options, sync); - // Find the first non-NodePackageImporter to pass as legacy `importer` option. - // NodePackageImporter will be passed in `modernOptions.importers`. - const importer = modernOptions.importers?.find( - _importer => !(_importer instanceof NodePackageImporter) - ) as Importer | FileImporter; + // Use a no-op base importer, because the LegacyImporterWrapper will emulate + // the base importer by itself in order to mark containingUrl as accessed. + const importer = modernOptions.importers?.some( + importer => importer instanceof LegacyImporterWrapper + ) + ? { + canonicalize() { + return null; + }, + load() { + return null; + }, + } + : undefined; return { ...modernOptions, diff --git a/lib/src/value/argument-list.ts b/lib/src/value/argument-list.ts index e5c41cb1..b898796a 100644 --- a/lib/src/value/argument-list.ts +++ b/lib/src/value/argument-list.ts @@ -31,6 +31,8 @@ export class SassArgumentList extends SassList { */ readonly keywordsInternal: OrderedMap; + private _keywordsAccessed = false; + /** * Whether the `keywords` getter has been accessed. * @@ -38,10 +40,12 @@ export class SassArgumentList extends SassList { * part of the package's public API and should not be accessed by user code. * It may be renamed or removed without warning in the future. */ - keywordsAccessed = false; + get keywordsAccessed(): boolean { + return this._keywordsAccessed; + } get keywords(): OrderedMap { - this.keywordsAccessed = true; + this._keywordsAccessed = true; return this.keywordsInternal; } diff --git a/lib/src/version.ts b/lib/src/version.ts new file mode 100644 index 00000000..772b8258 --- /dev/null +++ b/lib/src/version.ts @@ -0,0 +1,24 @@ +// 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 api from './vendor/sass'; + +export class Version implements api.Version { + constructor( + readonly major: number, + readonly minor: number, + readonly patch: number + ) {} + static parse(version: string): Version { + const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/); + if (match === null) { + throw new Error(`Invalid version ${version}`); + } + return new Version( + parseInt(match[1]), + parseInt(match[2]), + parseInt(match[3]) + ); + } +} diff --git a/npm/android-arm/package.json b/npm/android-arm/package.json index 66248754..31001aef 100644 --- a/npm/android-arm/package.json +++ b/npm/android-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm", - "version": "1.74.1", + "version": "1.77.3", "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 41f06166..849f79d7 100644 --- a/npm/android-arm64/package.json +++ b/npm/android-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-arm64", - "version": "1.74.1", + "version": "1.77.3", "description": "The android-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/android-ia32/package.json b/npm/android-ia32/package.json index 45453a15..beeeba0d 100644 --- a/npm/android-ia32/package.json +++ b/npm/android-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-ia32", - "version": "1.74.1", + "version": "1.77.3", "description": "The android-ia32 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 a83a3bf1..825f950c 100644 --- a/npm/android-x64/package.json +++ b/npm/android-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-android-x64", - "version": "1.74.1", + "version": "1.77.3", "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 d91f90f1..fcfc9f1d 100644 --- a/npm/darwin-arm64/package.json +++ b/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-arm64", - "version": "1.74.1", + "version": "1.77.3", "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 6cd713c3..a3b9b1e3 100644 --- a/npm/darwin-x64/package.json +++ b/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-darwin-x64", - "version": "1.74.1", + "version": "1.77.3", "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 57c3d1fb..45f56274 100644 --- a/npm/linux-arm/package.json +++ b/npm/linux-arm/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm", - "version": "1.74.1", + "version": "1.77.3", "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 bb3b2066..61d82dd5 100644 --- a/npm/linux-arm64/package.json +++ b/npm/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-arm64", - "version": "1.74.1", + "version": "1.77.3", "description": "The linux-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/linux-ia32/package.json b/npm/linux-ia32/package.json index 632ff253..82a27457 100644 --- a/npm/linux-ia32/package.json +++ b/npm/linux-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-ia32", - "version": "1.74.1", + "version": "1.77.3", "description": "The linux-ia32 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 ccdec118..0ed6b12c 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.74.1", + "version": "1.77.3", "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 0c9d14e3..c7192ad7 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.74.1", + "version": "1.77.3", "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 885bd534..a6726169 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.74.1", + "version": "1.77.3", "description": "The linux-musl-ia32 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 dca8e304..0d8d06d8 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.74.1", + "version": "1.77.3", "description": "The linux-musl-x64 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 f858a1d5..5db3156b 100644 --- a/npm/linux-x64/package.json +++ b/npm/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-linux-x64", - "version": "1.74.1", + "version": "1.77.3", "description": "The linux-x64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/win32-arm64/package.json b/npm/win32-arm64/package.json index feb70cb9..9f6ee597 100644 --- a/npm/win32-arm64/package.json +++ b/npm/win32-arm64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-arm64", - "version": "1.74.1", + "version": "1.77.3", "description": "The win32-arm64 binary for sass-embedded", "repository": "sass/embedded-host-node", "author": "Google Inc.", diff --git a/npm/win32-ia32/package.json b/npm/win32-ia32/package.json index 6abde34e..40fa2ee2 100644 --- a/npm/win32-ia32/package.json +++ b/npm/win32-ia32/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-ia32", - "version": "1.74.1", + "version": "1.77.3", "description": "The win32-ia32 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 e986cd9e..21561855 100644 --- a/npm/win32-x64/package.json +++ b/npm/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "sass-embedded-win32-x64", - "version": "1.74.1", + "version": "1.77.3", "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 f4104d16..8295fa64 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "sass-embedded", - "version": "1.74.1", + "version": "1.77.3", "protocol-version": "3.0.0-dev", - "compiler-version": "1.74.1", + "compiler-version": "1.77.3", "description": "Node.js library that communicates with Embedded Dart Sass using the Embedded Sass protocol", "repository": "sass/embedded-host-node", "author": "Google Inc.", @@ -35,23 +35,23 @@ "test": "jest" }, "optionalDependencies": { - "sass-embedded-android-arm": "1.74.1", - "sass-embedded-android-arm64": "1.74.1", - "sass-embedded-android-ia32": "1.74.1", - "sass-embedded-android-x64": "1.74.1", - "sass-embedded-darwin-arm64": "1.74.1", - "sass-embedded-darwin-x64": "1.74.1", - "sass-embedded-linux-arm": "1.74.1", - "sass-embedded-linux-arm64": "1.74.1", - "sass-embedded-linux-ia32": "1.74.1", - "sass-embedded-linux-x64": "1.74.1", - "sass-embedded-linux-musl-arm": "1.74.1", - "sass-embedded-linux-musl-arm64": "1.74.1", - "sass-embedded-linux-musl-ia32": "1.74.1", - "sass-embedded-linux-musl-x64": "1.74.1", - "sass-embedded-win32-arm64": "1.74.1", - "sass-embedded-win32-ia32": "1.74.1", - "sass-embedded-win32-x64": "1.74.1" + "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" }, "dependencies": { "@bufbuild/protobuf": "^1.0.0", @@ -78,7 +78,7 @@ "extract-zip": "^2.0.1", "gts": "^5.0.0", "jest": "^29.4.1", - "minipass": "7.0.4", + "minipass": "7.1.2", "npm-run-all": "^4.1.5", "shelljs": "^0.8.4", "simple-git": "^3.15.1", diff --git a/tool/get-deprecations.ts b/tool/get-deprecations.ts new file mode 100644 index 00000000..73618b4d --- /dev/null +++ b/tool/get-deprecations.ts @@ -0,0 +1,67 @@ +// Generates the list of deprecations from spec/deprecations.yaml in the +// language repo. + +import * as fs from 'fs'; +import {parse} from 'yaml'; + +interface YamlData { + [key: string]: { + description: string; + 'dart-sass': { + status: 'active' | 'future' | 'obsolete'; + deprecated?: string; + obsolete?: string; + }; + }; +} + +const yamlFile = 'build/sass/spec/deprecations.yaml'; + +/** + * Converts a version string in the form X.Y.Z to be code calling the Version + * constructor, or null if the string is undefined. + */ +function toVersionCode(version: string | undefined): string { + if (!version) return 'null'; + const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/); + if (match === null) { + throw new Error(`Invalid version ${version}`); + } + return `new Version(${match[1]}, ${match[2]}, ${match[3]})`; +} + +/** + * Generates the list of deprecations based on the YAML file in the language + * repo. + */ +export async function getDeprecations(outDirectory: string) { + const yamlText = fs.readFileSync(yamlFile, 'utf8'); + + const deprecations = parse(yamlText) as YamlData; + let tsText = + "import {Deprecations} from './sass';\n" + + "import {Version} from '../version';\n\n" + + 'export const deprecations: Deprecations = {\n'; + for (const [id, deprecation] of Object.entries(deprecations)) { + const key = id.includes('-') ? `'${id}'` : id; + const dartSass = deprecation['dart-sass']; + tsText += + ` ${key}: {\n` + + ` id: '${id}',\n` + + ` description: '${deprecation.description}',\n` + + ` status: '${dartSass.status}',\n` + + ` deprecatedIn: ${toVersionCode(dartSass.deprecated)},\n` + + ` obsoleteIn: ${toVersionCode(dartSass.obsolete)},\n` + + ' },\n'; + } + tsText += + " 'user-authored': {\n" + + " id: 'user-authored',\n" + + " status: 'user',\n" + + ' deprecatedIn: null,\n' + + ' obsoleteIn: null,\n' + + ' },\n' + + '}\n'; + + fs.writeFileSync(`${outDirectory}/deprecations.ts`, tsText); +} diff --git a/tool/init.ts b/tool/init.ts index a178675f..230cd09d 100644 --- a/tool/init.ts +++ b/tool/init.ts @@ -4,6 +4,7 @@ import yargs from 'yargs'; +import {getDeprecations} from './get-deprecations'; import {getEmbeddedCompiler} from './get-embedded-compiler'; import {getLanguageRepo} from './get-language-repo'; @@ -65,6 +66,8 @@ void (async () => { await getEmbeddedCompiler(outPath); } } + + await getDeprecations(outPath); } catch (error) { console.error(error); process.exitCode = 1;