Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge origin/main into feature.color-4 #302

Merged
merged 15 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand Down
32 changes: 32 additions & 0 deletions lib/src/canonicalize-context.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
157 changes: 6 additions & 151 deletions lib/src/deprecations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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,
},
};
33 changes: 20 additions & 13 deletions lib/src/importer-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -115,21 +116,22 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
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,
})
);
},
Expand Down Expand Up @@ -197,17 +199,21 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
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 ` +
Expand All @@ -216,6 +222,7 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
}
return new proto.InboundMessage_FileImportResponse({
result: {case: 'fileUrl', value: url.toString()},
containingUrlUnused: !canonicalizeContext.containingUrlAccessed,
});
}
);
Expand Down
26 changes: 25 additions & 1 deletion lib/src/legacy/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,34 @@ export class LegacyImporterWrapper<sync extends 'sync' | 'async'>

canonicalize(
url: string,
options: {fromImport: boolean}
options: {fromImport: boolean; containingUrl: URL | null}
): PromiseOr<URL | null, sync> {
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)
Expand Down
Loading
Loading