- Background
- Summary
- API
- Types
- Top-Level Members
- Embedded Protocol
This section is non-normative.
We recently added support to Dart Sass that allowed users to opt in to treating deprecation warnings as errors (on a per-deprecation basis), as well as opting in early to certain future deprecations. This is currently supported on the command line and via the Dart API, but we'd like to extend this support to the JS API as well.
We would also like to add support for silencing a particular deprecation's
warnings, primarily to enable a gentler process for deprecating @import
.
This section is non-normative.
This proposal adds a new Deprecation
interface and Version
class to the
JS API, three new optional properties on Options
(fatalDeprecations
,
silenceDeprecations
, and futureDeprecations
), a new parameter on
Logger.warn
(options.deprecationType
) two type aliases (DeprecationOrId
and DeprecationStatus
) and a new object deprecations
that contains the
various Deprecation
objects.
All deprecations are specified in deprecations
, and any new deprecations
added in the future (even those specific to a particular implementation)
should update the specification accordingly. Deprecations should never be
removed from the specification; when the behavior being deprecated is removed
(i.e. there's a major version release), the deprecation status should be
changed to obsolete, but remain in the specification.
Every Deprecation
has a unique id
, one of four status
values, and
(optionally) a human-readable description
. Depending on the status, each
deprecation may also have a deprecatedIn
version and an obsoleteIn
version that specify the compiler versions the deprecation became active
and became obsolete in, respectively.
One alternative to specifying a full Deprecation
interface is to just have
the relevant APIs take in string IDs. We considered this, but concluded that
each deprecation has additional metadata that users of the API may wish to
access (for example, a bundler may wish to surface the description
and
deprecatedIn
version to its users).
We chose to make the list of deprecations part of the specification itself, as this ensures that the language-wide deprecations are consistent across implementations. However, if an implementation wishes to add a deprecation that applies only to itself, it may still do so.
Additionally, while a deprecation's status is part of the specification, we
chose to leave the deprecatedIn
and obsoleteIn
versions of each
deprecation out of the specification. As the two current implementers of this
API are both based on Dart Sass, these versions are currently consistent
across implementations in practice, potential future implementers should not
need to be tied to Dart Sass's versioning.
Whenever potentially invalid sets of deprecations are passed to any of the options, we choose to emit warnings rather than errors, as the status of each deprecation can change over time, and users may share a configuration when compiling across multiple implementations/versions whose dependency statuses may not be in sync.
The situations we chose to warn for are:
-
an invalid string ID.
This is disallowed by the API's types, but may still occur at runtime, and should be warned for accordingly.
-
a future deprecation is passed to
fatalDeprecations
but notfutureDeprecations
.In this scenario, the future deprecation will still be treated as fatal, but we want to warn users to prevent situations where a user tries to make every deprecation fatal and ends up including future ones too.
-
an obsolete deprecation is passed to
fatalDeprecations
.If a deprecation is obsolete, that means the breaking change has already happened, so making it fatal is a no-op.
-
passing anything other than an active deprecation to
silenceDeprecations
.This is particularly important for obsolete deprecations, since otherwise users may not be aware of a subtle breaking change for which they were previously silencing warnings. We also warn for passing
Deprecation.userAuthored
, since there's no way to distinguish between different deprecations from user-authored code, so silencing them as a group is inadvisable. Passing a future deprecation here is either a no-op, or cancels out passing it tofutureDeprecations
, so we warn for that as well. -
passing a non-future deprecation to
futureDeprecations
.This is a no-op, so we should warn users so they can clean up their configuration.
import {SourceSpan} from '../spec/js-api';
declare module '../spec/js-api' {
interface Options<sync extends 'sync' | 'async'> {
A set of deprecations to treat as fatal.
If a deprecation warning of any provided type is encountered during compilation, the compiler must error instead.
The compiler should convert any string passed here to a Deprecation
by
indexing deprecations
. If an invalid deprecation ID is passed here, the
compiler must emit a warning. If a version is passed here, it should be treated
equivalently to passing all active deprecations whose deprecatedIn
version is
less than or equal to it.
The compiler must emit a warning if a future deprecation that's not also
included in futureDeprecations
or any obsolete deprecation is included here.
If a deprecation is passed both here and to silenceDeprecations
, a warning
must be emitted, but making the deprecation fatal must take precedence.
fatalDeprecations?: (DeprecationOrId | Version)[];
A set of active deprecations to ignore.
If a deprecation warning of any provided type is encountered during compilation, the compiler must ignore it.
The compiler should convert any string passed here to a Deprecation
by
indexing Deprecations
. If an invalid deprecation ID is passed here, the
compiler must emit a warning.
The compiler must emit a warning if any non-active deprecation is included here.
If a future deprecation is included both here and in futureDeprecations
, then
silencing it takes precedence.
silenceDeprecations?: DeprecationOrId[];
A set of future deprecations to opt into early.
For each future deprecation provided here, the compiler must treat that
deprecation as if it is active, emitting warnings as necessary (subject to
fatalDeprecations
and silenceDeprecations
).
The compiler should convert any string passed here to a Deprecation
by
indexing Deprecations
. If an invalid deprecation ID is passed here, the
compiler must emit a warning.
The compiler must emit a warning if a non-future deprecation is included here.
futureDeprecations?: DeprecationOrId[];
} // Options
interface Logger {
Update the third sub-bullet of bullet two to read:
If this warning is caused by behavior that used to be allowed but will be
disallowed in the future, set options.deprecation
to true
and set
options.deprecationType
to the relevant Deprecation
. Otherwise, set
options.deprecation
to false
and leave options.deprecationType
undefined.
warn?(
message: string,
options: {
deprecation: boolean;
deprecationType?: Deprecation;
span?: SourceSpan;
stack?: string;
}
): void;
} // Logger
} // module
interface Deprecations {
Deprecation for passing a string to call
instead of get-function
.
'call-string': Deprecation<'call-string'>;
Deprecation for @elseif
.
elseif: Deprecation<'elseif'>;
Deprecation for parsing @-moz-document
.
'moz-document': Deprecation<'moz-document'>;
Deprecation for importers using relative canonical URLs.
'relative-canonical': Deprecation<'relative-canonical'>;
Deprecation for declaring new variables with !global
.
'new-global': Deprecation<'new-global'>;
Deprecation for certain functions in the color module matching the behavior of their global counterparts for compatibility reasons.
'color-module-compat': Deprecation<'color-module-compat'>;
Deprecation for treating /
as division.
Update the proposal for forward slash as a separator to say that it emits deprecation warnings with ID 'slash-div'.
'slash-div': Deprecation<'slash-div'>;
Deprecation for leading, trailing, and repeated combinators.
Update the proposal for bogus combinators to say that it emits deprecation warnings with ID 'bogus-combinators'.
'bogus-combinators': Deprecation<'bogus-combinators'>;
Deprecation for ambiguous +
and -
operators.
Update the proposal for strict unary operators to say that it emits deprecation warnings with ID 'strict-unary'.
'strict-unary': Deprecation<'strict-unary'>;
Deprecation for passing invalid units to certain built-in functions.
Update the proposals for function units, random with units, and angle units to say that they emit deprecation warnings with ID 'function-units'.
'function-units': Deprecation<'function-units'>;
Deprecation for using multiple !global
or !default
flags on a single
variable.
This deprecation was never explicitly listed in a proposal.
'duplicate-var-flags': Deprecation<'duplicate-var-flags'>;
Deprecation for @import
rules.
Update the proposal for the module system to say that, when @import
is
deprecated, Sass will emit deprecation warnings with ID 'import' when @import
rules are encountered.
import: Deprecation<'import'>;
Used for deprecations coming from user-authored code.
'user-authored': Deprecation<'user-authored', 'user'>;
} // Deprecations
A deprecation, or the ID of one.
export type DeprecationOrId = Deprecation | keyof Deprecations;
A deprecation's status.
export type DeprecationStatus = 'active' | 'user' | 'future' | 'obsolete';
A deprecated feature in the language.
export interface Deprecation<
id extends keyof Deprecations = keyof Deprecations,
status extends DeprecationStatus = DeprecationStatus
> {
A kebab-case ID for this deprecation.
id: id;
The status of this deprecation.
- 'active' means this deprecation is currently enabled.
deprecatedIn
is non-null andobsoleteIn
is null. - 'user' means this deprecation is from user-authored code. Both
deprecatedIn
andobsoleteIn
are null. - 'future' means this deprecation is not yet enabled. Both
deprecatedIn
andobsoleteIn
are null. - 'obsolete' means this deprecation is now obsolete, as the feature it was for
has been fully removed. Both
deprecatedIn
andobsoleteIn
are non-null.
status: status;
A brief user-readable description of this deprecation.
description?: string;
The compiler version this feature was first deprecated in.
This is implementation-dependent, so versions are not guaranteed to be consistent between different compilers. For future deprecations, or those originating from user-authored code, this is null.
deprecatedIn: status extends 'future' | 'user' ? null : Version;
The compiler version this feature was fully removed in, making the deprecation obsolete.
This is null for active and future deprecations.
obsoleteIn: status extends 'obsolete' ? Version : null;
} // Deprecation
A semantic version of the compiler.
export class Version {
Creates a new Version
with its major
, minor
, and patch
fields set to the
corresponding arguments.
constructor(major: number, minor: number, patch: number);
The major version.
This must be a non-negative integer.
readonly major: number;
The minor version.
This must be a non-negative integer.
readonly minor: number;
The patch version.
This must be a non-negative integer.
readonly patch: number;
Parses a string in the form "major.minor.patch" into a Version
.
static parse(version: string): Version;
} // Version
declare module '../spec/js-api' {
An object containing all of the deprecations.
export const deprecations: Deprecations;
} // module
A set of deprecation IDs to treat as fatal.
If a deprecation warning of any provided type is encountered during compilation,
the compiler must respond with a CompileFailure
instead of a CompileSuccess
.
The compiler must emit an event of type LogEventType.WARNING
if any of the
following is true:
- an invalid deprecation ID is passed
- an obsolete deprecation ID is passed
- a future deprecation ID is passed that is not also passed to
future_deprecation
- a deprecation ID is passed both here and to
silence_deprecation
(making it fatal takes precedence)
repeated string fatal_deprecation = 15;
A set of deprecation IDs to ignore.
If a deprecation warning of any provided type is encountered during compilation, the compiler must ignore it.
The compiler must emit an event of type LogEventType.WARNING
if an invalid
deprecation ID or any non-active deprecation ID is passed here.
If a future deprecation ID is passed both here and to future_deprecation
, then
silencing it takes precedence.
repeated string silence_deprecation = 16;
A set of future deprecations IDs to opt into early.
For each future deprecation ID provided here, the compiler must treat that
deprecation as if it is active, emitting warnings as necessary (subject to
fatal_deprecation
and silence_deprecation
).
The compiler must emit an event of type LogEventType.WARNING
if an invalid
deprecation ID or any non-future deprecation ID is passed here.
repeated string future_deprecation = 17;
The deprecation ID for this warning, if type is
LogEventType.DEPRECATION_WARNING
.
optional string deprecation_type = 7;