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

Expose selector APIs through sass_api #1741

Merged
merged 2 commits into from
Jul 15, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/sass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export 'src/exception.dart' show SassException;
export 'src/importer.dart';
export 'src/logger.dart';
export 'src/syntax.dart';
export 'src/value.dart' hide ColorFormat, SassApiColor, SpanColorFormat;
export 'src/value.dart' hide ColorFormat, SassApiColor, SassApiValue, SpanColorFormat;
export 'src/visitor/serialize.dart' show OutputStyle;
export 'src/evaluation_context.dart' show warn;

Expand Down
5 changes: 5 additions & 0 deletions lib/src/ast/selector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../visitor/interface/selector.dart';
import '../visitor/serialize.dart';

Expand All @@ -25,9 +27,12 @@ export 'selector/universal.dart';
/// [ParentSelector] or a [PlaceholderSelector].
///
/// Selectors have structural equality semantics.
///
/// {@category Selector}
abstract class Selector {
/// Whether this selector, and complex selectors containing it, should not be
/// emitted.
@internal
bool get isInvisible => false;

/// Calls the appropriate visit method on [visitor].
Expand Down
5 changes: 5 additions & 0 deletions lib/src/ast/selector/attribute.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../visitor/interface/selector.dart';
import '../selector.dart';

/// An attribute selector.
///
/// This selects for elements with the given attribute, and optionally with a
/// value matching certain conditions as well.
///
/// {@category Selector}
@sealed
class AttributeSelector extends SimpleSelector {
/// The name of the attribute being selected for.
final QualifiedName name;
Expand Down
7 changes: 7 additions & 0 deletions lib/src/ast/selector/class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../visitor/interface/selector.dart';
import '../selector.dart';

/// A class selector.
///
/// This selects elements whose `class` attribute contains an identifier with
/// the given name.
///
/// {@category Selector}
@sealed
class ClassSelector extends SimpleSelector {
/// The class name this selects for.
final String name;
Expand All @@ -20,6 +25,8 @@ class ClassSelector extends SimpleSelector {

T accept<T>(SelectorVisitor<T> visitor) => visitor.visitClassSelector(this);

/// @nodoc
@internal
ClassSelector addSuffix(String suffix) => ClassSelector(name + suffix);

int get hashCode => name.hashCode;
Expand Down
30 changes: 30 additions & 0 deletions lib/src/ast/selector/complex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../extend/functions.dart';
import '../../logger.dart';
import '../../parse/selector.dart';
import '../../utils.dart';
import '../../visitor/interface/selector.dart';
import '../selector.dart';
Expand All @@ -11,6 +15,9 @@ import '../selector.dart';
///
/// A complex selector is composed of [CompoundSelector]s separated by
/// [Combinator]s. It selects elements based on their parent selectors.
///
/// {@category Selector}
@sealed
class ComplexSelector extends Selector {
/// The components of this selector.
///
Expand All @@ -25,6 +32,9 @@ class ComplexSelector extends Selector {
final List<ComplexSelectorComponent> components;

/// Whether a line break should be emitted *before* this selector.
///
/// @nodoc
@internal
final bool lineBreak;

/// The minimum possible specificity that this selector can have.
Expand All @@ -49,6 +59,8 @@ class ComplexSelector extends Selector {

int? _maxSpecificity;

/// @nodoc
@internal
late final bool isInvisible = components.any(
(component) => component is CompoundSelector && component.isInvisible);

Expand All @@ -60,6 +72,19 @@ class ComplexSelector extends Selector {
}
}

/// Parses a complex selector from [contents].
///
/// If passed, [url] is the name of the file from which [contents] comes.
/// [allowParent] controls whether a [ParentSelector] is allowed in this
/// selector.
///
/// Throws a [SassFormatException] if parsing fails.
factory ComplexSelector.parse(String contents,
{Object? url, Logger? logger, bool allowParent = true}) =>
SelectorParser(contents,
url: url, logger: logger, allowParent: allowParent)
.parseComplexSelector();

T accept<T>(SelectorVisitor<T> visitor) => visitor.visitComplexSelector(this);

/// Whether this is a superselector of [other].
Expand Down Expand Up @@ -92,10 +117,15 @@ class ComplexSelector extends Selector {
/// A component of a [ComplexSelector].
///
/// This is either a [CompoundSelector] or a [Combinator].
///
/// {@category Selector}
abstract class ComplexSelectorComponent {}

/// A combinator that defines the relationship between selectors in a
/// [ComplexSelector].
///
/// {@category Selector}
@sealed
class Combinator implements ComplexSelectorComponent {
/// Matches the right-hand selector if it's immediately adjacent to the
/// left-hand selector in the DOM tree.
Expand Down
7 changes: 7 additions & 0 deletions lib/src/ast/selector/compound.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../extend/functions.dart';
import '../../logger.dart';
import '../../parse/selector.dart';
Expand All @@ -13,6 +15,9 @@ import '../selector.dart';
///
/// A compound selector is composed of [SimpleSelector]s. It matches an element
/// that matches all of the component simple selectors.
///
/// {@category Selector}
@sealed
class CompoundSelector extends Selector implements ComplexSelectorComponent {
/// The components of this selector.
///
Expand Down Expand Up @@ -41,6 +46,8 @@ class CompoundSelector extends Selector implements ComplexSelectorComponent {

int? _maxSpecificity;

/// @nodoc
@internal
bool get isInvisible => components.any((component) => component.isInvisible);

CompoundSelector(Iterable<SimpleSelector> components)
Expand Down
9 changes: 9 additions & 0 deletions lib/src/ast/selector/id.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@

import 'dart:math' as math;

import 'package:meta/meta.dart';

import '../../visitor/interface/selector.dart';
import '../selector.dart';

/// An ID selector.
///
/// This selects elements whose `id` attribute exactly matches the given name.
///
/// {@category Selector}
@sealed
class IDSelector extends SimpleSelector {
/// The ID name this selects for.
final String name;
Expand All @@ -20,8 +25,12 @@ class IDSelector extends SimpleSelector {

T accept<T>(SelectorVisitor<T> visitor) => visitor.visitIDSelector(this);

/// @nodoc
@internal
IDSelector addSuffix(String suffix) => IDSelector(name + suffix);

/// @nodoc
@internal
List<SimpleSelector>? unify(List<SimpleSelector> compound) {
// A given compound selector may only contain one ID.
if (compound.any((simple) => simple is IDSelector && simple != this)) {
Expand Down
9 changes: 8 additions & 1 deletion lib/src/ast/selector/list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../extend/functions.dart';
import '../../logger.dart';
import '../../parse/selector.dart';
Expand All @@ -13,8 +15,11 @@ import '../selector.dart';

/// A selector list.
///
/// A selector list is composed of [ComplexSelector]s. It matches an element
/// A selector list is composed of [ComplexSelector]s. It matches any element
/// that matches any of the component selectors.
///
/// {@category Selector}
@sealed
class SelectorList extends Selector {
/// The components of this selector.
///
Expand All @@ -25,6 +30,8 @@ class SelectorList extends Selector {
bool get _containsParentSelector =>
components.any(_complexContainsParentSelector);

/// @nodoc
@internal
bool get isInvisible => components.every((complex) => complex.isInvisible);

/// Returns a SassScript list that represents this selector.
Expand Down
7 changes: 7 additions & 0 deletions lib/src/ast/selector/parent.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../visitor/interface/selector.dart';
import '../selector.dart';

/// A selector that matches the parent in the Sass stylesheet.
///
/// This is not a plain CSS selector—it should be removed before emitting a CSS
/// document.
///
/// {@category Selector}
@sealed
class ParentSelector extends SimpleSelector {
/// The suffix that will be added to the parent selector after it's been
/// resolved.
Expand All @@ -21,6 +26,8 @@ class ParentSelector extends SimpleSelector {

T accept<T>(SelectorVisitor<T> visitor) => visitor.visitParentSelector(this);

/// @nodoc
@internal
List<SimpleSelector> unify(List<SimpleSelector> compound) =>
throw UnsupportedError("& doesn't support unification.");
}
9 changes: 9 additions & 0 deletions lib/src/ast/selector/placeholder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import 'package:meta/meta.dart';

import '../../util/character.dart' as character;
import '../../visitor/interface/selector.dart';
import '../selector.dart';
Expand All @@ -11,10 +13,15 @@ import '../selector.dart';
/// This doesn't match any elements. It's intended to be extended using
/// `@extend`. It's not a plain CSS selector—it should be removed before
/// emitting a CSS document.
///
/// {@category Selector}
@sealed
class PlaceholderSelector extends SimpleSelector {
/// The name of the placeholder.
final String name;

/// @nodoc
@internal
bool get isInvisible => true;

/// Returns whether this is a private selector (that is, whether it begins
Expand All @@ -26,6 +33,8 @@ class PlaceholderSelector extends SimpleSelector {
T accept<T>(SelectorVisitor<T> visitor) =>
visitor.visitPlaceholderSelector(this);

/// @nodoc
@internal
PlaceholderSelector addSuffix(String suffix) =>
PlaceholderSelector(name + suffix);

Expand Down
16 changes: 16 additions & 0 deletions lib/src/ast/selector/pseudo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ import '../selector.dart';
/// selectors take arguments, including other selectors. Sass manually encodes
/// logic for each pseudo selector that takes a selector as an argument, to
/// ensure that extension and other selector operations work properly.
///
/// {@category Selector}
@sealed
class PseudoSelector extends SimpleSelector {
/// The name of this selector.
final String name;

/// Like [name], but without any vendor prefixes.
///
/// @nodoc
@internal
final String normalizedName;

/// Whether this is a pseudo-class selector.
Expand Down Expand Up @@ -49,10 +55,14 @@ class PseudoSelector extends SimpleSelector {
bool get isSyntacticElement => !isSyntacticClass;

/// Whether this is a valid `:host` selector.
///
/// @nodoc
@internal
bool get isHost => isClass && name == 'host';

/// Whether this is a valid `:host-context` selector.
///
/// @nodoc
@internal
bool get isHostContext =>
isClass && name == 'host-context' && selector != null;
Expand Down Expand Up @@ -83,6 +93,8 @@ class PseudoSelector extends SimpleSelector {

int? _maxSpecificity;

/// @nodoc
@internal
bool get isInvisible {
var selector = this.selector;
if (selector == null) return false;
Expand Down Expand Up @@ -128,11 +140,15 @@ class PseudoSelector extends SimpleSelector {
PseudoSelector withSelector(SelectorList selector) => PseudoSelector(name,
element: isElement, argument: argument, selector: selector);

/// @nodoc
@internal
PseudoSelector addSuffix(String suffix) {
if (argument != null || selector != null) super.addSuffix(suffix);
return PseudoSelector(name + suffix, element: isElement);
}

/// @nodoc
@internal
List<SimpleSelector>? unify(List<SimpleSelector> compound) {
if (name == 'host' || name == 'host-context') {
if (!compound.every((simple) =>
Expand Down
7 changes: 6 additions & 1 deletion lib/src/ast/selector/qualified_name.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

/// A [qualified name][].
import 'package:meta/meta.dart';

/// A [qualified name].
///
/// [qualified name]: https://www.w3.org/TR/css3-namespace/#css-qnames
///
/// {@category Selector}
@sealed
class QualifiedName {
/// The identifier name.
final String name;
Expand Down