Skip to content

Commit 07af8d6

Browse files
committed
Merge remote-tracking branch 'origin/main' into merge-main
2 parents 7d44ff3 + 82e1590 commit 07af8d6

File tree

29 files changed

+283
-210
lines changed

29 files changed

+283
-210
lines changed

CHANGELOG.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
## 1.77.3
2+
3+
### Dart API
4+
5+
* `Deprecation.duplicateVariableFlags` has been deprecated and replaced with
6+
`Deprecation.duplicateVarFlags` to make it consistent with the
7+
`duplicate-var-flags` name used on the command line and in the JS API.
8+
9+
## 1.77.2
10+
11+
* Don't emit deprecation warnings for functions and mixins beginning with `__`.
12+
13+
* Allow user-defined functions whose names begin with `_` and otherwise look
14+
like vendor-prefixed functions with special CSS syntax.
15+
16+
### Command-Line Interface
17+
18+
* Properly handle the `--silence-deprecation` flag.
19+
20+
* Handle the `--fatal-deprecation` and `--future-deprecation` flags for
21+
`--interactive` mode.
22+
23+
## 1.77.1
24+
25+
* Fix a crash that could come up with importers in certain contexts.
26+
27+
## 1.77.0
28+
29+
* *Don't* throw errors for at-rules in keyframe blocks.
30+
31+
## 1.76.0
32+
33+
* Throw errors for misplaced statements in keyframe blocks.
34+
35+
* Mixins and functions whose names begin with `--` are now deprecated for
36+
forwards-compatibility with the in-progress CSS functions and mixins spec.
37+
This deprecation is named `css-function-mixin`.
38+
39+
## 1.75.0
40+
41+
* Fix a bug in which stylesheet canonicalization could be cached incorrectly
42+
when custom importers or the Node.js package importer made decisions based on
43+
the URL of the containing stylesheet.
44+
45+
### JS API
46+
47+
* Allow `importer` to be passed without `url` in `StringOptionsWithImporter`.
48+
149
## 1.74.1
250

351
* No user-visible changes.

lib/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export {
4040
Deprecation,
4141
DeprecationOrId,
4242
DeprecationStatus,
43-
Version,
4443
} from './src/deprecations';
44+
export {Version} from './src/version';
4545
export {render, renderSync} from './src/legacy';
4646

4747
export const info = `sass-embedded\t${pkg.version}`;

lib/src/canonicalize-context.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2024 Google LLC. Use of this source code is governed by an
2+
// MIT-style license that can be found in the LICENSE file or at
3+
// https://opensource.org/licenses/MIT.
4+
5+
export class CanonicalizeContext {
6+
readonly fromImport: boolean;
7+
8+
private readonly _containingUrl: URL | null;
9+
10+
get containingUrl(): URL | null {
11+
this._containingUrlAccessed = true;
12+
return this._containingUrl;
13+
}
14+
15+
private _containingUrlAccessed = false;
16+
17+
/**
18+
* Whether the `containingUrl` getter has been accessed.
19+
*
20+
* This is marked as public so that the importer registry can access it, but
21+
* it's not part of the package's public API and should not be accessed by
22+
* user code. It may be renamed or removed without warning in the future.
23+
*/
24+
get containingUrlAccessed(): boolean {
25+
return this._containingUrlAccessed;
26+
}
27+
28+
constructor(containingUrl: URL | null, fromImport: boolean) {
29+
this._containingUrl = containingUrl;
30+
this.fromImport = fromImport;
31+
}
32+
}

lib/src/deprecations.ts

Lines changed: 6 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,17 @@
22
// MIT-style license that can be found in the LICENSE file or at
33
// https://opensource.org/licenses/MIT.
44

5-
import * as api from './vendor/sass';
5+
import {deprecations} from './vendor/deprecations';
6+
import {Deprecation, DeprecationOrId} from './vendor/sass';
7+
import {Version} from './version';
68

9+
export {deprecations} from './vendor/deprecations';
710
export {Deprecation, DeprecationOrId, DeprecationStatus} from './vendor/sass';
811

9-
export class Version implements api.Version {
10-
constructor(
11-
readonly major: number,
12-
readonly minor: number,
13-
readonly patch: number
14-
) {}
15-
static parse(version: string): Version {
16-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
17-
if (match === null) {
18-
throw new Error(`Invalid version ${version}`);
19-
}
20-
return new Version(
21-
parseInt(match[1]),
22-
parseInt(match[2]),
23-
parseInt(match[3])
24-
);
25-
}
26-
}
27-
2812
/**
2913
* Returns whether the given deprecation was active in the given version.
3014
*/
31-
function isActiveIn(deprecation: api.Deprecation, version: Version) {
15+
function isActiveIn(deprecation: Deprecation, version: Version) {
3216
const deprecatedIn = deprecation.deprecatedIn;
3317
if (deprecation.status !== 'active' || !deprecatedIn) return false;
3418
if (version.major > deprecatedIn.major) return true;
@@ -43,7 +27,7 @@ function isActiveIn(deprecation: api.Deprecation, version: Version) {
4327
* that's ready to include in a CompileRequest.
4428
*/
4529
export function getDeprecationIds(
46-
arr: (api.DeprecationOrId | Version)[]
30+
arr: (DeprecationOrId | Version)[]
4731
): string[] {
4832
return arr.flatMap(item => {
4933
if (item instanceof Version) {
@@ -56,132 +40,3 @@ export function getDeprecationIds(
5640
return item.id;
5741
});
5842
}
59-
60-
export const deprecations: typeof api.deprecations = {
61-
'call-string': {
62-
id: 'call-string',
63-
status: 'active',
64-
deprecatedIn: new Version(0, 0, 0),
65-
obsoleteIn: null,
66-
description: 'Passing a string directly to meta.call().',
67-
},
68-
elseif: {
69-
id: 'elseif',
70-
status: 'active',
71-
deprecatedIn: new Version(1, 3, 2),
72-
obsoleteIn: null,
73-
description: '@elseif.',
74-
},
75-
'moz-document': {
76-
id: 'moz-document',
77-
status: 'active',
78-
deprecatedIn: new Version(1, 7, 2),
79-
obsoleteIn: null,
80-
description: '@-moz-document.',
81-
},
82-
'relative-canonical': {
83-
id: 'relative-canonical',
84-
status: 'active',
85-
deprecatedIn: new Version(1, 14, 2),
86-
obsoleteIn: null,
87-
},
88-
'new-global': {
89-
id: 'new-global',
90-
status: 'active',
91-
deprecatedIn: new Version(1, 17, 2),
92-
obsoleteIn: null,
93-
description: 'Declaring new variables with !global.',
94-
},
95-
'color-module-compat': {
96-
id: 'color-module-compat',
97-
status: 'active',
98-
deprecatedIn: new Version(1, 23, 0),
99-
obsoleteIn: null,
100-
description:
101-
'Using color module functions in place of plain CSS functions.',
102-
},
103-
'slash-div': {
104-
id: 'slash-div',
105-
status: 'active',
106-
deprecatedIn: new Version(1, 33, 0),
107-
obsoleteIn: null,
108-
description: '/ operator for division.',
109-
},
110-
'bogus-combinators': {
111-
id: 'bogus-combinators',
112-
status: 'active',
113-
deprecatedIn: new Version(1, 54, 0),
114-
obsoleteIn: null,
115-
description: 'Leading, trailing, and repeated combinators.',
116-
},
117-
'strict-unary': {
118-
id: 'strict-unary',
119-
status: 'active',
120-
deprecatedIn: new Version(1, 55, 0),
121-
obsoleteIn: null,
122-
description: 'Ambiguous + and - operators.',
123-
},
124-
'function-units': {
125-
id: 'function-units',
126-
status: 'active',
127-
deprecatedIn: new Version(1, 56, 0),
128-
obsoleteIn: null,
129-
description: 'Passing invalid units to built-in functions.',
130-
},
131-
'duplicate-var-flags': {
132-
id: 'duplicate-var-flags',
133-
status: 'active',
134-
deprecatedIn: new Version(1, 62, 0),
135-
obsoleteIn: null,
136-
description: 'Using !default or !global multiple times for one variable.',
137-
},
138-
'null-alpha': {
139-
id: 'null-alpha',
140-
status: 'active',
141-
deprecatedIn: new Version(1, 62, 3),
142-
obsoleteIn: null,
143-
description: 'Passing null as alpha in the JS API.',
144-
},
145-
'abs-percent': {
146-
id: 'abs-percent',
147-
status: 'active',
148-
deprecatedIn: new Version(1, 65, 0),
149-
obsoleteIn: null,
150-
description: 'Passing percentages to the Sass abs() function.',
151-
},
152-
'fs-importer-cwd': {
153-
id: 'fs-importer-cwd',
154-
status: 'active',
155-
deprecatedIn: new Version(1, 73, 0),
156-
obsoleteIn: null,
157-
description:
158-
'Using the current working directory as an implicit load path.',
159-
},
160-
'color-4-api': {
161-
id: 'color-4-api',
162-
status: 'active',
163-
deprecatedIn: new Version(1, 76, 0),
164-
obsoleteIn: null,
165-
description: 'Methods of interacting with legacy SassColors.',
166-
},
167-
'color-functions': {
168-
id: 'color-functions',
169-
status: 'active',
170-
deprecatedIn: new Version(1, 76, 0),
171-
obsoleteIn: null,
172-
description: 'Using global Sass color functions.',
173-
},
174-
import: {
175-
id: 'import',
176-
status: 'future',
177-
deprecatedIn: null,
178-
obsoleteIn: null,
179-
description: '@import rules.',
180-
},
181-
'user-authored': {
182-
id: 'user-authored',
183-
status: 'user',
184-
deprecatedIn: null,
185-
obsoleteIn: null,
186-
},
187-
};

lib/src/importer-registry.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as p from 'path';
77
import {URL} from 'url';
88
import {inspect} from 'util';
99

10+
import {CanonicalizeContext} from './canonicalize-context';
1011
import * as utils from './utils';
1112
import {FileImporter, Importer, Options} from './vendor/sass';
1213
import * as proto from './vendor/embedded_sass_pb';
@@ -115,21 +116,22 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
115116
throw utils.compilerError('Unknown CanonicalizeRequest.importer_id');
116117
}
117118

119+
const canonicalizeContext = new CanonicalizeContext(
120+
request.containingUrl ? new URL(request.containingUrl) : null,
121+
request.fromImport
122+
);
123+
118124
return catchOr(
119125
() => {
120126
return thenOr(
121-
importer.canonicalize(request.url, {
122-
fromImport: request.fromImport,
123-
containingUrl: request.containingUrl
124-
? new URL(request.containingUrl)
125-
: null,
126-
}),
127+
importer.canonicalize(request.url, canonicalizeContext),
127128
url =>
128129
new proto.InboundMessage_CanonicalizeResponse({
129130
result:
130131
url === null
131132
? {case: undefined}
132133
: {case: 'url', value: url.toString()},
134+
containingUrlUnused: !canonicalizeContext.containingUrlAccessed,
133135
})
134136
);
135137
},
@@ -197,17 +199,21 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
197199
throw utils.compilerError('Unknown FileImportRequest.importer_id');
198200
}
199201

202+
const canonicalizeContext = new CanonicalizeContext(
203+
request.containingUrl ? new URL(request.containingUrl) : null,
204+
request.fromImport
205+
);
206+
200207
return catchOr(
201208
() => {
202209
return thenOr(
203-
importer.findFileUrl(request.url, {
204-
fromImport: request.fromImport,
205-
containingUrl: request.containingUrl
206-
? new URL(request.containingUrl)
207-
: null,
208-
}),
210+
importer.findFileUrl(request.url, canonicalizeContext),
209211
url => {
210-
if (!url) return new proto.InboundMessage_FileImportResponse();
212+
if (!url) {
213+
return new proto.InboundMessage_FileImportResponse({
214+
containingUrlUnused: !canonicalizeContext.containingUrlAccessed,
215+
});
216+
}
211217
if (url.protocol !== 'file:') {
212218
throw (
213219
`FileImporter ${inspect(importer)} returned non-file: URL ` +
@@ -216,6 +222,7 @@ export class ImporterRegistry<sync extends 'sync' | 'async'> {
216222
}
217223
return new proto.InboundMessage_FileImportResponse({
218224
result: {case: 'fileUrl', value: url.toString()},
225+
containingUrlUnused: !canonicalizeContext.containingUrlAccessed,
219226
});
220227
}
221228
);

lib/src/legacy/importer.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,34 @@ export class LegacyImporterWrapper<sync extends 'sync' | 'async'>
8686

8787
canonicalize(
8888
url: string,
89-
options: {fromImport: boolean}
89+
options: {fromImport: boolean; containingUrl: URL | null}
9090
): PromiseOr<URL | null, sync> {
9191
if (url.startsWith(endOfLoadProtocol)) return new URL(url);
9292

93+
// Emulate a base importer instead of using a real base importer,
94+
// because we want to mark containingUrl as used, which is impossible
95+
// in a real base importer.
96+
if (options.containingUrl !== null) {
97+
try {
98+
const absoluteUrl = new URL(url, options.containingUrl).toString();
99+
const resolved = this.canonicalize(absoluteUrl, {
100+
fromImport: options.fromImport,
101+
containingUrl: null,
102+
});
103+
if (resolved !== null) return resolved;
104+
} catch (error: unknown) {
105+
if (
106+
error instanceof TypeError &&
107+
isErrnoException(error) &&
108+
error.code === 'ERR_INVALID_URL'
109+
) {
110+
// ignore
111+
} else {
112+
throw error;
113+
}
114+
}
115+
}
116+
93117
if (
94118
url.startsWith(legacyImporterProtocolPrefix) ||
95119
url.startsWith(legacyImporterProtocol)

0 commit comments

Comments
 (0)