mirror of
https://github.com/danog/dart-sass.git
synced 2024-12-04 10:37:52 +01:00
Track extenders by complex selector.
This allows us to share more logic if selector lists overlap.
This commit is contained in:
parent
4173ffc901
commit
fe286487bd
@ -23,7 +23,7 @@ class Extender {
|
|||||||
|
|
||||||
/// A map from all extended simple selectors to the sources of those
|
/// A map from all extended simple selectors to the sources of those
|
||||||
/// extensions.
|
/// extensions.
|
||||||
final _extensions = <SimpleSelector, Map<SelectorList, ExtendState>>{};
|
final _extensions = <SimpleSelector, Map<ComplexSelector, ExtendState>>{};
|
||||||
|
|
||||||
/// An expando from [CssStyleRule] to media query contexts.
|
/// An expando from [CssStyleRule] to media query contexts.
|
||||||
///
|
///
|
||||||
@ -55,24 +55,22 @@ class Extender {
|
|||||||
/// This works as though `source {@extend target}` were written in
|
/// This works as though `source {@extend target}` were written in
|
||||||
/// the stylesheet.
|
/// the stylesheet.
|
||||||
static SelectorList extend(
|
static SelectorList extend(
|
||||||
SelectorList selector, SelectorList source, SimpleSelector target) =>
|
SelectorList selector, SelectorList source, SimpleSelector target) {
|
||||||
new Extender()._extendList(
|
var extenders = new Map<ComplexSelector, ExtendState>.fromIterable(
|
||||||
selector,
|
source.components,
|
||||||
{
|
value: (_) => new ExtendState.oneOff());
|
||||||
target: {source: new ExtendState.oneOff()}
|
return new Extender()._extendList(selector, {target: extenders}, null);
|
||||||
},
|
}
|
||||||
null);
|
|
||||||
|
|
||||||
/// Returns a copy of [selector] with [source] replaced by [target].
|
/// Returns a copy of [selector] with [source] replaced by [target].
|
||||||
static SelectorList replace(
|
static SelectorList replace(
|
||||||
SelectorList selector, SelectorList source, SimpleSelector target) =>
|
SelectorList selector, SelectorList source, SimpleSelector target) {
|
||||||
new Extender()._extendList(
|
var extenders = new Map<ComplexSelector, ExtendState>.fromIterable(
|
||||||
selector,
|
source.components,
|
||||||
{
|
value: (_) => new ExtendState.oneOff());
|
||||||
target: {source: new ExtendState.oneOff()}
|
return new Extender()
|
||||||
},
|
._extendList(selector, {target: extenders}, null, replace: true);
|
||||||
null,
|
}
|
||||||
replace: true);
|
|
||||||
|
|
||||||
/// Adds [selector] to this extender, associated with [span].
|
/// Adds [selector] to this extender, associated with [span].
|
||||||
///
|
///
|
||||||
@ -137,22 +135,25 @@ class Extender {
|
|||||||
void addExtension(
|
void addExtension(
|
||||||
SelectorList extender, SimpleSelector target, ExtendRule extend,
|
SelectorList extender, SimpleSelector target, ExtendRule extend,
|
||||||
[List<CssMediaQuery> mediaContext]) {
|
[List<CssMediaQuery> mediaContext]) {
|
||||||
|
var rules = _selectors[target];
|
||||||
|
|
||||||
|
Map<ComplexSelector, ExtendState> newExtenders;
|
||||||
var sources = _extensions.putIfAbsent(target, () => {});
|
var sources = _extensions.putIfAbsent(target, () => {});
|
||||||
var existingState = sources[extender];
|
|
||||||
if (existingState != null) {
|
|
||||||
// If there's already an extend from [extender] to [target], we don't need
|
|
||||||
// to re-run the extension. We may need to mark the extension as
|
|
||||||
// mandatory, though.
|
|
||||||
existingState.addSource(extend.span, mediaContext,
|
|
||||||
optional: extend.isOptional);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var state =
|
|
||||||
new ExtendState(extend.span, mediaContext, optional: extend.isOptional);
|
|
||||||
sources[extender] = state;
|
|
||||||
|
|
||||||
for (var complex in extender.components) {
|
for (var complex in extender.components) {
|
||||||
|
var existingState = sources[complex];
|
||||||
|
if (existingState != null) {
|
||||||
|
// If there's already an extend from [extender] to [target], we don't need
|
||||||
|
// to re-run the extension. We may need to mark the extension as
|
||||||
|
// mandatory, though.
|
||||||
|
existingState.addSource(extend.span, mediaContext,
|
||||||
|
optional: extend.isOptional);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = new ExtendState(extend.span, mediaContext,
|
||||||
|
optional: extend.isOptional);
|
||||||
|
sources[complex] = state;
|
||||||
|
|
||||||
for (var component in complex.components) {
|
for (var component in complex.components) {
|
||||||
if (component is CompoundSelector) {
|
if (component is CompoundSelector) {
|
||||||
for (var simple in component.components) {
|
for (var simple in component.components) {
|
||||||
@ -160,17 +161,19 @@ class Extender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rules != null) {
|
||||||
|
newExtenders ??= {};
|
||||||
|
newExtenders[complex] = state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var rules = _selectors[target];
|
|
||||||
if (rules == null) return;
|
if (rules == null) return;
|
||||||
var extensions = {
|
if (newExtenders == null) return;
|
||||||
target: {extender: state}
|
|
||||||
};
|
|
||||||
for (var rule in rules) {
|
for (var rule in rules) {
|
||||||
try {
|
try {
|
||||||
rule.selector.value =
|
rule.selector.value = _extendList(
|
||||||
_extendList(rule.selector.value, extensions, _mediaContexts[rule]);
|
rule.selector.value, {target: newExtenders}, _mediaContexts[rule]);
|
||||||
} on SassException catch (error) {
|
} on SassException catch (error) {
|
||||||
throw new SassException(
|
throw new SassException(
|
||||||
"From ${rule.selector.span.message('')}\n"
|
"From ${rule.selector.span.message('')}\n"
|
||||||
@ -201,7 +204,7 @@ class Extender {
|
|||||||
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
||||||
SelectorList _extendList(
|
SelectorList _extendList(
|
||||||
SelectorList list,
|
SelectorList list,
|
||||||
Map<SimpleSelector, Map<SelectorList, ExtendState>> extensions,
|
Map<SimpleSelector, Map<ComplexSelector, ExtendState>> extensions,
|
||||||
List<CssMediaQuery> mediaQueryContext,
|
List<CssMediaQuery> mediaQueryContext,
|
||||||
{bool replace: false}) {
|
{bool replace: false}) {
|
||||||
// This could be written more simply using [List.map], but we want to avoid
|
// This could be written more simply using [List.map], but we want to avoid
|
||||||
@ -230,7 +233,7 @@ class Extender {
|
|||||||
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
||||||
List<List<ComplexSelector>> _extendComplex(
|
List<List<ComplexSelector>> _extendComplex(
|
||||||
ComplexSelector complex,
|
ComplexSelector complex,
|
||||||
Map<SimpleSelector, Map<SelectorList, ExtendState>> extensions,
|
Map<SimpleSelector, Map<ComplexSelector, ExtendState>> extensions,
|
||||||
List<CssMediaQuery> mediaQueryContext,
|
List<CssMediaQuery> mediaQueryContext,
|
||||||
{bool replace: false}) {
|
{bool replace: false}) {
|
||||||
// This could be written more simply using [List.map], but we want to avoid
|
// This could be written more simply using [List.map], but we want to avoid
|
||||||
@ -290,7 +293,7 @@ class Extender {
|
|||||||
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
||||||
List<ComplexSelector> _extendCompound(
|
List<ComplexSelector> _extendCompound(
|
||||||
CompoundSelector compound,
|
CompoundSelector compound,
|
||||||
Map<SimpleSelector, Map<SelectorList, ExtendState>> extensions,
|
Map<SimpleSelector, Map<ComplexSelector, ExtendState>> extensions,
|
||||||
List<CssMediaQuery> mediaQueryContext,
|
List<CssMediaQuery> mediaQueryContext,
|
||||||
{bool replace: false}) {
|
{bool replace: false}) {
|
||||||
var original = compound;
|
var original = compound;
|
||||||
@ -312,35 +315,31 @@ class Extender {
|
|||||||
compoundWithoutSimple.setRange(0, i, compound.components);
|
compoundWithoutSimple.setRange(0, i, compound.components);
|
||||||
compoundWithoutSimple.setRange(
|
compoundWithoutSimple.setRange(
|
||||||
i, compound.components.length - 1, compound.components, i + 1);
|
i, compound.components.length - 1, compound.components, i + 1);
|
||||||
sources.forEach((extender, state) {
|
sources.forEach((complex, state) {
|
||||||
for (var complex in extender.components) {
|
var extenderBase = complex.components.last as CompoundSelector;
|
||||||
var extenderBase = complex.components.last as CompoundSelector;
|
var unified = compoundWithoutSimple.isEmpty
|
||||||
var unified = compoundWithoutSimple.isEmpty
|
? extenderBase
|
||||||
? extenderBase
|
: unifyCompound(extenderBase.components, compoundWithoutSimple);
|
||||||
: unifyCompound(extenderBase.components, compoundWithoutSimple);
|
if (unified == null) return;
|
||||||
if (unified == null) continue;
|
|
||||||
|
|
||||||
state.assertCompatibleMediaContext(mediaQueryContext);
|
state.assertCompatibleMediaContext(mediaQueryContext);
|
||||||
|
|
||||||
extended ??= replace
|
extended ??= replace
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
new ComplexSelector([compound])
|
new ComplexSelector([compound])
|
||||||
];
|
];
|
||||||
|
|
||||||
var newComplex = new ComplexSelector(
|
var newComplex = new ComplexSelector(
|
||||||
complex.components
|
complex.components
|
||||||
.sublist(0, complex.components.length - 1)
|
.sublist(0, complex.components.length - 1)
|
||||||
.toList()
|
.toList()
|
||||||
..add(unified),
|
..add(unified),
|
||||||
lineBreak: complex.lineBreak);
|
lineBreak: complex.lineBreak);
|
||||||
_addSourceSpecificity(
|
_addSourceSpecificity(newComplex,
|
||||||
newComplex,
|
math.max(_sourceSpecificityFor(compound), complex.maxSpecificity));
|
||||||
math.max(
|
extended.add(newComplex);
|
||||||
_sourceSpecificityFor(compound), complex.maxSpecificity));
|
state.isUsed = true;
|
||||||
extended.add(newComplex);
|
|
||||||
state.isUsed = true;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ class Extender {
|
|||||||
CompoundSelector compound,
|
CompoundSelector compound,
|
||||||
int i,
|
int i,
|
||||||
SimpleSelector simple,
|
SimpleSelector simple,
|
||||||
Map<SimpleSelector, Map<SelectorList, ExtendState>> extensions,
|
Map<SimpleSelector, Map<ComplexSelector, ExtendState>> extensions,
|
||||||
List<CssMediaQuery> mediaQueryContext,
|
List<CssMediaQuery> mediaQueryContext,
|
||||||
{bool replace: false}) {
|
{bool replace: false}) {
|
||||||
if (simple is PseudoSelector && simple.selector != null) {
|
if (simple is PseudoSelector && simple.selector != null) {
|
||||||
@ -397,7 +396,7 @@ class Extender {
|
|||||||
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
/// If [replace] is `true`, this doesn't preserve the original selectors.
|
||||||
List<PseudoSelector> _extendPseudo(
|
List<PseudoSelector> _extendPseudo(
|
||||||
PseudoSelector pseudo,
|
PseudoSelector pseudo,
|
||||||
Map<SimpleSelector, Map<SelectorList, ExtendState>> extensions,
|
Map<SimpleSelector, Map<ComplexSelector, ExtendState>> extensions,
|
||||||
List<CssMediaQuery> mediaQueryContext,
|
List<CssMediaQuery> mediaQueryContext,
|
||||||
{bool replace: false}) {
|
{bool replace: false}) {
|
||||||
var extended = _extendList(pseudo.selector, extensions, mediaQueryContext,
|
var extended = _extendList(pseudo.selector, extensions, mediaQueryContext,
|
||||||
|
Loading…
Reference in New Issue
Block a user