From f32712a011ba102259a21c7e99e3872957580559 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 27 Feb 2020 18:36:25 -0800 Subject: [PATCH 1/8] Add a README explaining which specs live in this directory --- spec/core_functions/selector/extend/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 spec/core_functions/selector/extend/README.md diff --git a/spec/core_functions/selector/extend/README.md b/spec/core_functions/selector/extend/README.md new file mode 100644 index 0000000000..91ec3ee655 --- /dev/null +++ b/spec/core_functions/selector/extend/README.md @@ -0,0 +1,7 @@ +Much of the complexity in the algorithm for extend comes from determining which +selectors are superselectors of which others and unifying two selectors, which +are covered more explicitly by specs for the `is-superselector()` and +`selector-unify()` functions, respectively. To avoid unnecessary duplication, +the specs for `selector-extend()` itself make an effort to thoroughly exercise +the superselector or unification logic, and instead focuses on behavior that's +specific to the full extension process. From 85b246b56760105a5a50aa1fd00beb420a8be80b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 26 Feb 2020 13:31:26 -0800 Subject: [PATCH 2/8] Add no-op specs for selector-extend() --- spec/core_functions/selector/extend/no_op.hrx | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 spec/core_functions/selector/extend/no_op.hrx diff --git a/spec/core_functions/selector/extend/no_op.hrx b/spec/core_functions/selector/extend/no_op.hrx new file mode 100644 index 0000000000..f77b5a7e09 --- /dev/null +++ b/spec/core_functions/selector/extend/no_op.hrx @@ -0,0 +1,227 @@ +<===> missing/input.scss +a {b: selector-extend("c", "d", "e")} + +<===> missing/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> conflict/element/alone/input.scss +a {b: selector-extend("c.d", ".d", "e")} + +<===> conflict/element/alone/output.css +a { + b: c.d; +} + +<===> +================================================================================ +<===> conflict/element/with_class/input.scss +a {b: selector-extend("c.d", ".d", "e.f")} + +<===> conflict/element/with_class/output.css +a { + b: c.d; +} + +<===> +================================================================================ +<===> conflict/id/input.scss +a {b: selector-extend("#c.d", ".d", "#e")} + +<===> conflict/id/output.css +a { + b: #c.d; +} + +<===> +================================================================================ +<===> conflict/pseudo_element/unknown/input.scss +a {b: selector-extend("::c.d", ".d", "::e")} + +<===> conflict/pseudo_element/unknown/output.css +a { + b: ::c.d; +} + +<===> +================================================================================ +<===> conflict/pseudo_element/class_syntax/input.scss +a {b: selector-extend(":before.c", ".c", ":after")} + +<===> conflict/pseudo_element/class_syntax/output.css +a { + b: :before.c; +} + +<===> +================================================================================ +<===> conflict/universal/namespace_and_namespace/input.scss +a {b: selector-extend("c|*.d", ".d", "e|*")} + +<===> conflict/universal/namespace_and_namespace/output.css +a { + b: c|*.d; +} + +<===> +================================================================================ +<===> conflict/universal/namespace_and_empty/input.scss +a {b: selector-extend("c|*.d", ".d", "|*")} + +<===> conflict/universal/namespace_and_empty/output.css +a { + b: c|*.d; +} + +<===> +================================================================================ +<===> conflict/universal/empty_and_namespace/input.scss +a {b: selector-extend("|*.c", ".c", "d|*")} + +<===> conflict/universal/empty_and_namespace/output.css +a { + b: |*.c; +} + +<===> +================================================================================ +<===> conflict/universal/namespace_and_default/input.scss +a {b: selector-extend("c|*.d", ".d", "*")} + +<===> conflict/universal/namespace_and_default/output.css +a { + b: c|*.d; +} + +<===> +================================================================================ +<===> conflict/universal/default_and_namespace/input.scss +a {b: selector-extend("*.c", ".c", "d|*")} + +<===> conflict/universal/default_and_namespace/output.css +a { + b: *.c; +} + +<===> +================================================================================ +<===> conflict/universal/empty_and_default/input.scss +a {b: selector-extend("|*.c", ".c", "*")} + +<===> conflict/universal/empty_and_default/output.css +a { + b: |*.c; +} + +<===> +================================================================================ +<===> conflict/universal/default_and_empty/input.scss +a {b: selector-extend("*.c", ".c", "|*")} + +<===> conflict/universal/default_and_empty/output.css +a { + b: *.c; +} + +<===> +================================================================================ +<===> conflict/parent/input.scss +a {b: selector-extend("c > .d", ".d", "e > .f")} + +<===> conflict/parent/output.css +a { + b: c > .d; +} + +<===> +================================================================================ +<===> conflict/next_sibling/input.scss +a {b: selector-extend("c + .d", ".d", "e + .f")} + +<===> conflict/next_sibling/output.css +a { + b: c + .d; +} + +<===> +================================================================================ +<===> unification/identical_to_extendee/input.scss +a {b: selector-extend("c.d", ".d", ".d")} + +<===> unification/identical_to_extendee/output.css +a { + b: c.d; +} + +<===> +================================================================================ +<===> unification/identical_to_selector/input.scss +a {b: selector-extend("c.d", ".d", "c.d")} + +<===> unification/identical_to_selector/output.css +a { + b: c.d; +} + +<===> +================================================================================ +<===> unification/additional/simple/input.scss +a {b: selector-extend("c", "c", "c.d")} + +<===> unification/additional/simple/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> unification/additional/ancestor/input.scss +a {b: selector-extend("c", "c", "d c")} + +<===> unification/additional/ancestor/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> unification/additional/parent/input.scss +a {b: selector-extend("c", "c", "d > c")} + +<===> unification/additional/parent/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> unification/additional/sibling/input.scss +a {b: selector-extend("c", "c", "d ~ c")} + +<===> unification/additional/sibling/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> unification/additional/next_sibling/input.scss +a {b: selector-extend("c", "c", "d + c")} + +<===> unification/additional/next_sibling/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> unification/subselector_of_target/input.scss +a {b: selector-extend(".c:matches(d)", ":matches(d)", "d.e")} + +<===> unification/subselector_of_target/output.css +a { + b: .c:matches(d); +} From e5867bf647da0d48463f8464d3a6fd6909f9500d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 26 Feb 2020 13:46:03 -0800 Subject: [PATCH 3/8] Add error specs for selector-extend() --- spec/core_functions/selector/extend/error.hrx | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 spec/core_functions/selector/extend/error.hrx diff --git a/spec/core_functions/selector/extend/error.hrx b/spec/core_functions/selector/extend/error.hrx new file mode 100644 index 0000000000..1c73e1b005 --- /dev/null +++ b/spec/core_functions/selector/extend/error.hrx @@ -0,0 +1,291 @@ +<===> selector/parent/input.scss +a {b: selector-extend("&", "c", "d")} + +<===> selector/parent/error +Error: $selector: Parent selectors aren't allowed here. + , +1 | & + | ^ + ' + - 1:1 root stylesheet + , +1 | a {b: selector-extend("&", "c", "d")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> selector/parent/error-libsass +Error: Parent selectors aren't allowed here. + on line 1:23 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("&", "c", "d")} + + ----------------------^ + +<===> +================================================================================ +<===> selector/invalid/input.scss +a {b: selector-extend("[c", "d", "e")} + +<===> selector/invalid/error +Error: $selector: expected more input. + , +1 | [c + | ^ + ' + - 1:3 root stylesheet + , +1 | a {b: selector-extend("[c", "d", "e")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> selector/invalid/error-libsass +Error: invalid operator in attribute selector for c + on line 1:24 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("[c", "d", "e")} + + -----------------------^ + +<===> +================================================================================ +<===> selector/type/options.yml +--- +:todo: +- sass/libsass#2964 + +<===> selector/type/input.scss +a {b: selector-extend(1, "c", "d")} + +<===> selector/type/error +Error: $selector: 1 is not a valid selector: it must be a string, +a list of strings, or a list of lists of strings. + , +1 | a {b: selector-extend(1, "c", "d")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> +================================================================================ +<===> extendee/parent/input.scss +a {b: selector-extend("c", "&", "d")} + +<===> extendee/parent/error +Error: $extendee: Parent selectors aren't allowed here. + , +1 | & + | ^ + ' + - 1:1 root stylesheet + , +1 | a {b: selector-extend("c", "&", "d")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> extendee/parent/error-libsass +Error: Parent selectors aren't allowed here. + on line 1:28 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("c", "&", "d")} + + ---------------------------^ + +<===> +================================================================================ +<===> extendee/complex/options.yml +--- +:todo: +- sass/libsass#3066 + +<===> +================================================================================ +<===> extendee/complex/string/input.scss +a {b: selector-extend("c", "d e", "f")} + +<===> extendee/complex/string/error +Error: Can't extend complex selector d e. + , +1 | a {b: selector-extend("c", "d e", "f")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> +================================================================================ +<===> extendee/complex/list/input.scss +a {b: selector-extend("c", d e, "f")} + +<===> extendee/complex/list/error +Error: Can't extend complex selector d e. + , +1 | a {b: selector-extend("c", d e, "f")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> +================================================================================ +<===> extendee/invalid/input.scss +a {b: selector-extend("c", "[d", "e")} + +<===> extendee/invalid/error +Error: $extendee: expected more input. + , +1 | [d + | ^ + ' + - 1:3 root stylesheet + , +1 | a {b: selector-extend("c", "[d", "e")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> extendee/invalid/error-libsass +Error: invalid operator in attribute selector for d + on line 1:29 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("c", "[d", "e")} + + ----------------------------^ + +<===> +================================================================================ +<===> extendee/type/options.yml +--- +:todo: +- sass/libsass#2964 + +<===> extendee/type/input.scss +a {b: selector-extend("c", 1, "d")} + +<===> extendee/type/error +Error: $extendee: 1 is not a valid selector: it must be a string, +a list of strings, or a list of lists of strings. + , +1 | a {b: selector-extend("c", 1, "d")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> +================================================================================ +<===> extender/parent/input.scss +a {b: selector-extend("c", "d", "&")} + +<===> extender/parent/error +Error: $extender: Parent selectors aren't allowed here. + , +1 | & + | ^ + ' + - 1:1 root stylesheet + , +1 | a {b: selector-extend("c", "d", "&")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> extender/parent/error-libsass +Error: Parent selectors aren't allowed here. + on line 1:33 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("c", "d", "&")} + + --------------------------------^ + +<===> +================================================================================ +<===> extender/invalid/input.scss +a {b: selector-extend("c", "d", "[e")} + +<===> extender/invalid/error +Error: $extender: expected more input. + , +1 | [e + | ^ + ' + - 1:3 root stylesheet + , +1 | a {b: selector-extend("c", "d", "[e")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> extender/invalid/error-libsass +Error: invalid operator in attribute selector for e + on line 1:34 of input.scss, in function `selector-extend` + from line 1:7 of input.scss +>> a {b: selector-extend("c", "d", "[e")} + + ---------------------------------^ + +<===> +================================================================================ +<===> extender/type/options.yml +--- +:todo: +- sass/libsass#2964 + +<===> extender/type/input.scss +a {b: selector-extend("c", "d", 1)} + +<===> extender/type/error +Error: $extender: 1 is not a valid selector: it must be a string, +a list of strings, or a list of lists of strings. + , +1 | a {b: selector-extend("c", "d", 1)} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ' + input.scss 1:7 root stylesheet + +<===> +================================================================================ +<===> too_many_args/input.scss +a {b: selector-extend("c", "d", "e", "f")} + +<===> too_many_args/error +Error: Only 3 arguments allowed, but 4 were passed. + ,--> input.scss +1 | a {b: selector-extend("c", "d", "e", "f")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invocation + ' + ,--> sass:selector +1 | @function extend($selector, $extendee, $extender) { + | ======================================= declaration + ' + input.scss 1:7 root stylesheet + +<===> too_many_args/error-libsass +Error: wrong number of arguments (4 for 3) for `selector-extend' + on line 1:7 of input.scss +>> a {b: selector-extend("c", "d", "e", "f")} + + ------^ + +<===> +================================================================================ +<===> too_few_args/input.scss +a {b: selector-extend("c", "d")} + +<===> too_few_args/error +Error: Missing argument $extender. + ,--> input.scss +1 | a {b: selector-extend("c", "d")} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ invocation + ' + ,--> sass:selector +1 | @function extend($selector, $extendee, $extender) { + | ======================================= declaration + ' + input.scss 1:7 root stylesheet + +<===> too_few_args/error-libsass +Error: Function selector-extend is missing argument $extender. + on line 1 of input.scss +>> a {b: selector-extend("c", "d")} + + ------^ From ff7f1cacb9e0345fcbd92bea4fd15035658718ef Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 26 Feb 2020 15:42:48 -0800 Subject: [PATCH 4/8] Add specs for input formats --- spec/core_functions/selector/extend/input.hrx | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 spec/core_functions/selector/extend/input.hrx diff --git a/spec/core_functions/selector/extend/input.hrx b/spec/core_functions/selector/extend/input.hrx new file mode 100644 index 0000000000..99f38156e9 --- /dev/null +++ b/spec/core_functions/selector/extend/input.hrx @@ -0,0 +1,79 @@ +<===> non_string/README.md +These specs verify that all the arguments to `selector-extend()` can take the +generalized selector format. The full set of possible input formats is tested +with `selector-parse()`; this spec just verifies one example for each parameter. + +<===> +================================================================================ +<===> non_string/selector/input.scss +a {b: selector-extend((c, d c), "c", "e")} + +<===> non_string/selector/output.css +a { + b: c, e, d c; +} + +<===> +================================================================================ +<===> non_string/extendee/options.yml +--- +:todo: +- sass/libsass#3068 + +<===> non_string/extendee/input.scss +a {b: selector-extend("c.d", (c, ".d"), ".e")} + +<===> non_string/extendee/output.css +a { + b: c.d, .e; +} + +<===> +================================================================================ +<===> non_string/extender/input.scss +a {b: selector-extend("c", "c", (d, e f))} + +<===> non_string/extender/output.css +a { + b: c, d, e f; +} + +<===> +================================================================================ +<===> multiple_extendees/compound/input.scss +a {b: selector-extend("c.d", "c.d", ".e")} + +<===> multiple_extendees/compound/output.css +a { + b: c.d, .e; +} + +<===> +================================================================================ +<===> multiple_extendees/list/options.yml +--- +:todo: +- sass/libsass#3067 + +<===> multiple_extendees/list/input.scss +a {b: selector-extend("c.d", "c, .d", ".e")} + +<===> multiple_extendees/list/output.css +a { + b: c.d, .e; +} + +<===> +================================================================================ +<===> multiple_extendees/list_of_compound/options.yml +--- +:todo: +- sass/libsass#3067 + +<===> multiple_extendees/list_of_compound/input.scss +a {b: selector-extend("c.d.e.f", "c.d, .e.f", ".g")} + +<===> multiple_extendees/list_of_compound/output.css +a { + b: c.d.e.f, .g; +} From 0b56d63b19a25d8798440a4cb273522f5b5d66c2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 26 Feb 2020 17:05:55 -0800 Subject: [PATCH 5/8] Add specs for matching simple selectors --- .../selector/extend/simple/README.md | 1 + .../selector/extend/simple/attribute.hrx | 37 +++ .../selector/extend/simple/class.hrx | 17 + .../selector/extend/simple/id.hrx | 17 + .../selector/extend/simple/placeholder.hrx | 17 + .../selector/extend/simple/pseudo/arg.hrx | 84 +++++ .../selector/extend/simple/pseudo/no_arg.hrx | 57 ++++ .../extend/simple/pseudo/selector_arg.hrx | 161 ++++++++++ .../selector/extend/simple/type.hrx | 163 ++++++++++ .../selector/extend/simple/universal.hrx | 293 ++++++++++++++++++ 10 files changed, 847 insertions(+) create mode 100644 spec/core_functions/selector/extend/simple/README.md create mode 100644 spec/core_functions/selector/extend/simple/attribute.hrx create mode 100644 spec/core_functions/selector/extend/simple/class.hrx create mode 100644 spec/core_functions/selector/extend/simple/id.hrx create mode 100644 spec/core_functions/selector/extend/simple/placeholder.hrx create mode 100644 spec/core_functions/selector/extend/simple/pseudo/arg.hrx create mode 100644 spec/core_functions/selector/extend/simple/pseudo/no_arg.hrx create mode 100644 spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx create mode 100644 spec/core_functions/selector/extend/simple/type.hrx create mode 100644 spec/core_functions/selector/extend/simple/universal.hrx diff --git a/spec/core_functions/selector/extend/simple/README.md b/spec/core_functions/selector/extend/simple/README.md new file mode 100644 index 0000000000..90a8e3d730 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/README.md @@ -0,0 +1 @@ +"Simple" as in "simple selectors", not as in "simple test cases". diff --git a/spec/core_functions/selector/extend/simple/attribute.hrx b/spec/core_functions/selector/extend/simple/attribute.hrx new file mode 100644 index 0000000000..e03c9efea0 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/attribute.hrx @@ -0,0 +1,37 @@ +<===> equal/input.scss +a {b: selector-extend("[c=d]", "[c=d]", "e")} + +<===> equal/output.css +a { + b: [c=d], e; +} + +<===> +================================================================================ +<===> unequal/name/input.scss +a {b: selector-extend("[c=d]", "[e=d]", "f")} + +<===> unequal/name/output.css +a { + b: [c=d]; +} + +<===> +================================================================================ +<===> unequal/value/input.scss +a {b: selector-extend("[c=d]", "[c=e]", "f")} + +<===> unequal/value/output.css +a { + b: [c=d]; +} + +<===> +================================================================================ +<===> unequal/operator/input.scss +a {b: selector-extend("[c=d]", "[c^=d]", "f")} + +<===> unequal/operator/output.css +a { + b: [c=d]; +} diff --git a/spec/core_functions/selector/extend/simple/class.hrx b/spec/core_functions/selector/extend/simple/class.hrx new file mode 100644 index 0000000000..8faececf46 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/class.hrx @@ -0,0 +1,17 @@ +<===> equal/input.scss +a {b: selector-extend(".c", ".c", "e")} + +<===> equal/output.css +a { + b: .c, e; +} + +<===> +================================================================================ +<===> unequal/input.scss +a {b: selector-extend(".c", ".d", "e")} + +<===> unequal/output.css +a { + b: .c; +} diff --git a/spec/core_functions/selector/extend/simple/id.hrx b/spec/core_functions/selector/extend/simple/id.hrx new file mode 100644 index 0000000000..9b6e533c41 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/id.hrx @@ -0,0 +1,17 @@ +<===> equal/input.scss +a {b: selector-extend("#c", "#c", "e")} + +<===> equal/output.css +a { + b: #c, e; +} + +<===> +================================================================================ +<===> unequal/input.scss +a {b: selector-extend("#c", "#d", "e")} + +<===> unequal/output.css +a { + b: #c; +} diff --git a/spec/core_functions/selector/extend/simple/placeholder.hrx b/spec/core_functions/selector/extend/simple/placeholder.hrx new file mode 100644 index 0000000000..23f7749535 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/placeholder.hrx @@ -0,0 +1,17 @@ +<===> equal/input.scss +a {b: selector-extend("%c", "%c", "e")} + +<===> equal/output.css +a { + b: %c, e; +} + +<===> +================================================================================ +<===> unequal/input.scss +a {b: selector-extend("%c", "%d", "e")} + +<===> unequal/output.css +a { + b: %c; +} diff --git a/spec/core_functions/selector/extend/simple/pseudo/arg.hrx b/spec/core_functions/selector/extend/simple/pseudo/arg.hrx new file mode 100644 index 0000000000..c1fb18f57c --- /dev/null +++ b/spec/core_functions/selector/extend/simple/pseudo/arg.hrx @@ -0,0 +1,84 @@ +<===> options.yml +--- +:todo: +- sass/libsass#3068 + +<===> +================================================================================ +<===> class/equal/input.scss +a {b: selector-extend(":c(@#$)", ":c(@#$)", "e")} + +<===> class/equal/output.css +a { + b: :c(@#$), e; +} + +<===> +================================================================================ +<===> class/unequal/name/input.scss +a {b: selector-extend(":c(@#$)", ":d(@#$)", "e")} + +<===> class/unequal/name/output.css +a { + b: :c(@#$); +} + +<===> +================================================================================ +<===> class/unequal/argument/input.scss +a {b: selector-extend(":c(@#$)", ":c(*&^)", "e")} + +<===> class/unequal/argument/output.css +a { + b: :c(@#$); +} + +<===> +================================================================================ +<===> class/unequal/has_argument/input.scss +a {b: selector-extend(":c(@#$)", ":c", "e")} + +<===> class/unequal/has_argument/output.css +a { + b: :c(@#$); +} + +<===> +================================================================================ +<===> element/equal/input.scss +a {b: selector-extend("::c(@#$)", "::c(@#$)", "e")} + +<===> element/equal/output.css +a { + b: ::c(@#$), e; +} + +<===> +================================================================================ +<===> element/unequal/name/input.scss +a {b: selector-extend("::c(@#$)", ":d(@#$)", "e")} + +<===> element/unequal/name/output.css +a { + b: ::c(@#$); +} + +<===> +================================================================================ +<===> element/unequal/argument/input.scss +a {b: selector-extend("::c(@#$)", "::c(*&^)", "e")} + +<===> element/unequal/argument/output.css +a { + b: ::c(@#$); +} + +<===> +================================================================================ +<===> element/unequal/has_argument/input.scss +a {b: selector-extend("::c(@#$)", "::c", "e")} + +<===> element/unequal/has_argument/output.css +a { + b: ::c(@#$); +} diff --git a/spec/core_functions/selector/extend/simple/pseudo/no_arg.hrx b/spec/core_functions/selector/extend/simple/pseudo/no_arg.hrx new file mode 100644 index 0000000000..68fc1b5ac1 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/pseudo/no_arg.hrx @@ -0,0 +1,57 @@ +<===> class/equal/input.scss +a {b: selector-extend(":c", ":c", "e")} + +<===> class/equal/output.css +a { + b: :c, e; +} + +<===> +================================================================================ +<===> class/unequal/input.scss +a {b: selector-extend(":c", ":d", "e")} + +<===> class/unequal/output.css +a { + b: :c; +} + +<===> +================================================================================ +<===> class/and_element/input.scss +a {b: selector-extend(":c", "::c", "e")} + +<===> class/and_element/output.css +a { + b: :c; +} + +<===> +================================================================================ +<===> element/equal/input.scss +a {b: selector-extend("::c", "::c", "e")} + +<===> element/equal/output.css +a { + b: ::c, e; +} + +<===> +================================================================================ +<===> element/unequal/input.scss +a {b: selector-extend("::c", "::d", "e")} + +<===> element/unequal/output.css +a { + b: ::c; +} + +<===> +================================================================================ +<===> element/and_class/input.scss +a {b: selector-extend("::c", ":c", "e")} + +<===> element/and_class/output.css +a { + b: ::c; +} diff --git a/spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx b/spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx new file mode 100644 index 0000000000..972a050f25 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx @@ -0,0 +1,161 @@ +<===> README.md +For the purposes of figuring out which simple selectors to extend, +`selector-extend` doesn't care about the semantics of particular pseudo +selectors. All it looks at is whether they're structurally equal. + +<===> +================================================================================ +<===> unprefixed/class/equal/input.scss +a {b: selector-extend(":matches(c d.e, f g)", ":matches(c d.e, f g)", "h")} + +<===> unprefixed/class/equal/output.css +a { + b: :matches(c d.e, f g), h; +} + +<===> +================================================================================ +<===> unprefixed/class/unequal/name/input.scss +a {b: selector-extend(":matches(c d.e, f g)", ":-pfx-matches(c d.e, f g)", "h")} + +<===> unprefixed/class/unequal/name/output.css +a { + b: :matches(c d.e, f g); +} + +<===> +================================================================================ +<===> unprefixed/class/unequal/argument/input.scss +a {b: selector-extend(":matches(c d.e, f g)", ":matches(d, g)", "h")} + +<===> unprefixed/class/unequal/argument/output.css +a { + b: :matches(c d.e, f g); +} + +<===> +================================================================================ +<===> unprefixed/class/unequal/has_argument/input.scss +a {b: selector-extend(":matches(c d.e, f g)", ":matches", "h")} + +<===> unprefixed/class/unequal/has_argument/output.css +a { + b: :matches(c d.e, f g); +} + +<===> +================================================================================ +<===> unprefixed/element/equal/input.scss +a {b: selector-extend("::slotted(c.d, e.f)", "::slotted(c.d, e.f)", "g")} + +<===> unprefixed/element/equal/output.css +a { + b: ::slotted(c.d, e.f), g; +} + +<===> +================================================================================ +<===> unprefixed/element/unequal/name/input.scss +a {b: selector-extend("::slotted(c.d, e.f)", "::-pfx-slotted(c.d, e.f)", "g")} + +<===> unprefixed/element/unequal/name/output.css +a { + b: ::slotted(c.d, e.f); +} + +<===> +================================================================================ +<===> unprefixed/element/unequal/argument/input.scss +a {b: selector-extend("::slotted(c.d, e.f)", "::slotted(d, g)", "g")} + +<===> unprefixed/element/unequal/argument/output.css +a { + b: ::slotted(c.d, e.f); +} + +<===> +================================================================================ +<===> unprefixed/element/unequal/has_argument/input.scss +a {b: selector-extend("::slotted(c.d, e.f)", "::slotted", "g")} + +<===> unprefixed/element/unequal/has_argument/output.css +a { + b: ::slotted(c.d, e.f); +} + +<===> +================================================================================ +<===> prefixed/options.yml +--- +:todo: +- sass/libsass#2961 + +<===> +================================================================================ +<===> prefixed/equal/input.scss +a { + b: selector-extend( + ":nth-child(2n + 1 of c d.e, f g)", + ":nth-child(2n + 1 of c d.e, f g)", + "h"); +} + +<===> prefixed/equal/output.css +a { + b: :nth-child(2n+1 of c d.e, f g), h; +} + +<===> +================================================================================ +<===> prefixed/unequal/name/input.scss +a { + b: selector-extend( + ":nth-child(2n + 1 of c d.e, f g)", + ":nth-last-child(2n + 1 of c d.e, f g)", + "h"); +} + +<===> prefixed/unequal/name/output.css +a { + b: :nth-child(2n+1 of c d.e, f g); +} + +<===> +================================================================================ +<===> prefixed/unequal/argument/input.scss +a { + b: selector-extend( + ":nth-child(2n + 1 of c d.e, f g)", + ":nth-child(2n + 1 of d, g)", + "h"); +} + +<===> prefixed/unequal/argument/output.css +a { + b: :nth-child(2n+1 of c d.e, f g); +} + +<===> +================================================================================ +<===> prefixed/unequal/prefix/input.scss +a { + b: selector-extend( + ":nth-child(2n + 1 of c d.e, f g)", + ":nth-child(2n of c d.e, f g)", + "h"); +} + +<===> prefixed/unequal/prefix/output.css +a { + b: :nth-child(2n+1 of c d.e, f g); +} + +<===> +================================================================================ +<===> prefixed/unequal/has_argument/input.scss +a {b: selector-extend(":nth-child(2n + 1 of c d.e, f g)", ":nth-child", "h")} + +<===> prefixed/unequal/has_argument/output.css +a { + b: :nth-child(2n+1 of c d.e, f g); +} diff --git a/spec/core_functions/selector/extend/simple/type.hrx b/spec/core_functions/selector/extend/simple/type.hrx new file mode 100644 index 0000000000..2b25b6d7d5 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/type.hrx @@ -0,0 +1,163 @@ +<===> equal/input.scss +a {b: selector-extend("c", "c", "e")} + +<===> equal/output.css +a { + b: c, e; +} + +<===> +================================================================================ +<===> unequal/input.scss +a {b: selector-extend("c", "d", "e")} + +<===> unequal/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> and_universal/input.scss +a {b: selector-extend("c", "*", "d")} + +<===> and_universal/output.css +a { + b: c; +} + +<===> +================================================================================ +<===> namespace/explicit/and_explicit/equal/input.scss +a {b: selector-extend("c|d", "c|d", "e")} + +<===> namespace/explicit/and_explicit/equal/output.css +a { + b: c|d, e; +} + +<===> +================================================================================ +<===> namespace/explicit/and_explicit/unequal/input.scss +a {b: selector-extend("c|d", "e|d", "e")} + +<===> namespace/explicit/and_explicit/unequal/output.css +a { + b: c|d; +} + +<===> +================================================================================ +<===> namespace/explicit/and_implicit/input.scss +a {b: selector-extend("c|d", "d", "e")} + +<===> namespace/explicit/and_implicit/output.css +a { + b: c|d; +} + +<===> +================================================================================ +<===> namespace/explicit/and_empty/input.scss +a {b: selector-extend("c|d", "|d", "e")} + +<===> namespace/explicit/and_empty/output.css +a { + b: c|d; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/input.scss +a {b: selector-extend("c|d", "*|d", "e")} + +<===> namespace/explicit/and_universal/output.css +a { + b: c|d; +} + +<===> +================================================================================ +<===> namespace/empty/and_explicit/input.scss +a {b: selector-extend("|c", "d|c", "e")} + +<===> namespace/empty/and_explicit/output.css +a { + b: |c; +} + +<===> +================================================================================ +<===> namespace/empty/and_implicit/input.scss +a {b: selector-extend("|c", "c", "d")} + +<===> namespace/empty/and_implicit/output.css +a { + b: |c; +} + +<===> +================================================================================ +<===> namespace/empty/and_empty/input.scss +a {b: selector-extend("|c", "|c", "d")} + +<===> namespace/empty/and_empty/output.css +a { + b: |c, d; +} + +<===> +================================================================================ +<===> namespace/empty/and_universal/input.scss +a {b: selector-extend("|c", "*|c", "d")} + +<===> namespace/empty/and_universal/output.css +a { + b: |c; +} + +<===> +================================================================================ +<===> namespace/universal/README.md +Although many of these extendees are subselectors of the targets, they're not +matched because this phase only cares about full structural equality. + +<===> +================================================================================ +<===> namespace/universal/and_explicit/input.scss +a {b: selector-extend("*|c", "d|c", "d")} + +<===> namespace/universal/and_explicit/output.css +a { + b: *|c; +} + +<===> +================================================================================ +<===> namespace/universal/and_implicit/input.scss +a {b: selector-extend("*|c", "c", "d")} + +<===> namespace/universal/and_implicit/output.css +a { + b: *|c; +} + +<===> +================================================================================ +<===> namespace/universal/and_empty/input.scss +a {b: selector-extend("*|c", "|c", "d")} + +<===> namespace/universal/and_empty/output.css +a { + b: *|c; +} + +<===> +================================================================================ +<===> namespace/universal/and_universal/input.scss +a {b: selector-extend("*|c", "*|c", "d")} + +<===> namespace/universal/and_universal/output.css +a { + b: *|c, d; +} diff --git a/spec/core_functions/selector/extend/simple/universal.hrx b/spec/core_functions/selector/extend/simple/universal.hrx new file mode 100644 index 0000000000..a260cae8f3 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/universal.hrx @@ -0,0 +1,293 @@ +<===> README.md +Although many of these extendees are subselectors of the targets, they're not +matched because this phase only cares about full structural equality. + +<===> +================================================================================ +<===> equal/input.scss +a {b: selector-extend("*", "*", "c")} + +<===> equal/output.css +a { + b: *, c; +} + +<===> +================================================================================ +<===> and_type/input.scss +a {b: selector-extend("*", "c", "d")} + +<===> and_type/output.css +a { + b: *; +} + +<===> +================================================================================ +<===> and_class/input.scss +a {b: selector-extend("*", ".c", "d")} + +<===> and_class/output.css +a { + b: *; +} + +<===> +================================================================================ +<===> namespace/explicit/and_type/explicit/equal/input.scss +a {b: selector-extend("c|*", "c|d", "e")} + +<===> namespace/explicit/and_type/explicit/equal/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_type/explicit/unequal/input.scss +a {b: selector-extend("c|*", "e|d", "e")} + +<===> namespace/explicit/and_type/explicit/unequal/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_type/implicit/input.scss +a {b: selector-extend("c|*", "d", "e")} + +<===> namespace/explicit/and_type/implicit/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_type/empty/input.scss +a {b: selector-extend("c|*", "|d", "e")} + +<===> namespace/explicit/and_type/empty/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/explicit/equal/input.scss +a {b: selector-extend("c|*", "c|*", "e")} + +<===> namespace/explicit/and_universal/explicit/equal/output.css +a { + b: c|*, e; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/explicit/unequal/input.scss +a {b: selector-extend("c|*", "d|*", "e")} + +<===> namespace/explicit/and_universal/explicit/unequal/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/implicit/input.scss +a {b: selector-extend("c|*", "*", "e")} + +<===> namespace/explicit/and_universal/implicit/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/empty/input.scss +a {b: selector-extend("c|*", "|*", "e")} + +<===> namespace/explicit/and_universal/empty/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_universal/universal/input.scss +a {b: selector-extend("c|*", "*|*", "e")} + +<===> namespace/explicit/and_universal/universal/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/explicit/and_class/input.scss +a {b: selector-extend("c|*", ".d", "e")} + +<===> namespace/explicit/and_class/output.css +a { + b: c|*; +} + +<===> +================================================================================ +<===> namespace/empty/and_type/explicit/input.scss +a {b: selector-extend("|*", "c|d", "e")} + +<===> namespace/empty/and_type/explicit/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_type/implicit/input.scss +a {b: selector-extend("|*", "d", "e")} + +<===> namespace/empty/and_type/implicit/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_type/empty/input.scss +a {b: selector-extend("|*", "|d", "e")} + +<===> namespace/empty/and_type/empty/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_universal/explicit/input.scss +a {b: selector-extend("|*", "c|*", "d")} + +<===> namespace/empty/and_universal/explicit/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_universal/implicit/input.scss +a {b: selector-extend("|*", "*", "c")} + +<===> namespace/empty/and_universal/implicit/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_universal/empty/input.scss +a {b: selector-extend("|*", "|*", "c")} + +<===> namespace/empty/and_universal/empty/output.css +a { + b: |*, c; +} + +<===> +================================================================================ +<===> namespace/empty/and_universal/universal/input.scss +a {b: selector-extend("|*", "*|*", "c")} + +<===> namespace/empty/and_universal/universal/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/empty/and_class/input.scss +a {b: selector-extend("|*", ".c", "d")} + +<===> namespace/empty/and_class/output.css +a { + b: |*; +} + +<===> +================================================================================ +<===> namespace/universal/and_type/explicit/input.scss +a {b: selector-extend("*|*", "c|d", "e")} + +<===> namespace/universal/and_type/explicit/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_type/implicit/input.scss +a {b: selector-extend("*|*", "c", "d")} + +<===> namespace/universal/and_type/implicit/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_type/empty/input.scss +a {b: selector-extend("*|*", "|c", "d")} + +<===> namespace/universal/and_type/empty/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_universal/explicit/input.scss +a {b: selector-extend("*|*", "c|*", "d")} + +<===> namespace/universal/and_universal/explicit/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_universal/implicit/input.scss +a {b: selector-extend("*|*", "*", "c")} + +<===> namespace/universal/and_universal/implicit/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_universal/empty/input.scss +a {b: selector-extend("*|*", "|*", "c")} + +<===> namespace/universal/and_universal/empty/output.css +a { + b: *|*; +} + +<===> +================================================================================ +<===> namespace/universal/and_universal/universal/input.scss +a {b: selector-extend("*|*", "*|*", "c")} + +<===> namespace/universal/and_universal/universal/output.css +a { + b: *|*, c; +} + +<===> +================================================================================ +<===> namespace/universal/and_class/input.scss +a {b: selector-extend("*|*", ".c", "d")} + +<===> namespace/universal/and_class/output.css +a { + b: *|*; +} From e27d2a9e7633c22ad489f7df73cc83bd80fad5c0 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 27 Feb 2020 17:03:52 -0800 Subject: [PATCH 6/8] Add more specs for selector pseudos --- .../simple/pseudo/selector/idempotent.hrx | 384 ++++++++++++++++++ .../{selector_arg.hrx => selector/match.hrx} | 7 +- .../simple/pseudo/selector/non_idempotent.hrx | 123 ++++++ 3 files changed, 511 insertions(+), 3 deletions(-) create mode 100644 spec/core_functions/selector/extend/simple/pseudo/selector/idempotent.hrx rename spec/core_functions/selector/extend/simple/pseudo/{selector_arg.hrx => selector/match.hrx} (94%) create mode 100644 spec/core_functions/selector/extend/simple/pseudo/selector/non_idempotent.hrx diff --git a/spec/core_functions/selector/extend/simple/pseudo/selector/idempotent.hrx b/spec/core_functions/selector/extend/simple/pseudo/selector/idempotent.hrx new file mode 100644 index 0000000000..49a74818f2 --- /dev/null +++ b/spec/core_functions/selector/extend/simple/pseudo/selector/idempotent.hrx @@ -0,0 +1,384 @@ +<===> README.md +Nesting one of these selector pseudoclasses within an identical selector +pseudoclass has no additional effect. As such, Sass flattens these nestings when +`@extend` would otherwise cause them to occur. + +<===> +================================================================================ +<===> not/README.md +Unlike other pseudo-selectors, when extending into `:not()` we add additional +`:not()`s to the compound selector, rather than adding selectors within the +original `:not()`. We're able to do this because (uniquely among selector +pseudos) `:not(X, Y)` is equivalent to `:not(X):not(Y)`. We *want* to do this +because older browsers only support a compound selector within `:not()` rather +than a selector list. + +<===> +================================================================================ +<===> not/simple/input.scss +a {b: selector-extend(":not(.c)", ".c", ".d")} + +<===> not/simple/output.css +a { + b: :not(.c):not(.d); +} + +<===> +================================================================================ +<===> not/list/input.scss +a {b: selector-extend(":not(.c)", ".c", ".d, .e")} + +<===> not/list/output.css +a { + b: :not(.c):not(.d):not(.e); +} + +<===> +================================================================================ +<===> not/complex/input.scss +a {b: selector-extend(":not(.c .d)", ".d", ".e .f")} + +<===> not/complex/output.css +a { + b: :not(.c .d):not(.c .e .f):not(.e .c .f); +} + +<===> +================================================================================ +<===> not/component/input.scss +a {b: selector-extend(":not(.c.d)", ".c", ".e")} + +<===> not/component/output.css +a { + b: :not(.c.d):not(.d.e); +} + +<===> +================================================================================ +<===> not/list_in_not/input.scss +// If the original :not() already contains a selector list, we add new selectors +// to that list because there's no risk of breaking additional browsers. +a {b: selector-extend(":not(.c, .d)", ".c", ".e")} + +<===> not/list_in_not/output.css +a { + b: :not(.c, .e, .d); +} + +<===> +================================================================================ +<===> not/matches/list/input.scss +a {b: selector-extend(":not(.c)", ".c", ":matches(.d, .e)")} + +<===> not/matches/list/output.css +a { + b: :not(.c):not(.d):not(.e); +} + +<===> +================================================================================ +<===> not/matches/list_of_complex/input.scss +a {b: selector-extend(":not(.c)", ".c", ":matches(.d .e, .f .g)")} + +<===> not/matches/list_of_complex/output.css +a { + b: :not(.c):not(.d .e):not(.f .g); +} + +<===> +================================================================================ +<===> not/matches/in_compound/input.scss +a {b: selector-extend(":not(.c)", ".c", ".d:matches(.e, .f)")} + +<===> not/matches/in_compound/output.css +a { + b: :not(.c):not(.d:matches(.e, .f)); +} + +<===> +================================================================================ +<===> not/not_in_extender/input.scss +// Ideally, this should emit `.d, :not(.c)`, but that would be the only +// situation where extending a pseudo selector could produce a full-on selector +// list. For the sake of simplicity of the `@extend` algorithm, we just ignore +// nested `:not()`s instead. +a {b: selector-extend(":not(.c)", ".c", ":not(.d)")} + +<===> not/not_in_extender/output.css +a { + b: :not(.c); +} + +<===> +================================================================================ +<===> matches/simple/input.scss +a {b: selector-extend(":matches(.c)", ".c", ".d")} + +<===> matches/simple/output.css +a { + b: :matches(.c, .d); +} + +<===> +================================================================================ +<===> matches/list/input.scss +a {b: selector-extend(":matches(.c)", ".c", ".d, .e")} + +<===> matches/list/output.css +a { + b: :matches(.c, .d, .e); +} + +<===> +================================================================================ +<===> matches/matches_in_extender/options.yml +--- +:todo: +- sass/libsass#3069 + +<===> matches/matches_in_extender/input.scss +a {b: selector-extend(":matches(.c)", ".c", ":matches(.d, .e)")} + +<===> matches/matches_in_extender/output.css +a { + b: :matches(.c, .d, .e); +} + +<===> +================================================================================ +<===> any/simple/input.scss +a {b: selector-extend(":any(.c)", ".c", ".d")} + +<===> any/simple/output.css +a { + b: :any(.c, .d); +} + +<===> +================================================================================ +<===> any/list/input.scss +a {b: selector-extend(":any(.c)", ".c", ".d, .e")} + +<===> any/list/output.css +a { + b: :any(.c, .d, .e); +} + +<===> +================================================================================ +<===> any/any_in_extender/options.yml +--- +:todo: +- sass/libsass#3069 + +<===> any/any_in_extender/input.scss +a {b: selector-extend(":any(.c)", ".c", ":any(.d, .e)")} + +<===> any/any_in_extender/output.css +a { + b: :any(.c, .d, .e); +} + +<===> +================================================================================ +<===> current/simple/input.scss +a {b: selector-extend(":current(.c)", ".c", ".d")} + +<===> current/simple/output.css +a { + b: :current(.c, .d); +} + +<===> +================================================================================ +<===> current/list/input.scss +a {b: selector-extend(":current(.c)", ".c", ".d, .e")} + +<===> current/list/output.css +a { + b: :current(.c, .d, .e); +} + +<===> +================================================================================ +<===> current/current_in_extender/options.yml +--- +:todo: +- sass/libsass#3069 + +<===> current/current_in_extender/input.scss +a {b: selector-extend(":current(.c)", ".c", ":current(.d, .e)")} + +<===> current/current_in_extender/output.css +a { + b: :current(.c, .d, .e); +} + +<===> +================================================================================ +<===> nth_child/options.yml +--- +:todo: +- sass/libsass#2961 + +<===> +================================================================================ +<===> nth_child/simple/input.scss +a {b: selector-extend(":nth-child(2n + 1 of .c)", ".c", ".d")} + +<===> nth_child/simple/output.css +a { + b: :nth-child(2n+1 of .c, .d); +} + +<===> +================================================================================ +<===> nth_child/list/input.scss +a {b: selector-extend(":nth-child(2n + 1 of .c)", ".c", ".d, .e")} + +<===> nth_child/list/output.css +a { + b: :nth-child(2n+1 of .c, .d, .e); +} + +<===> +================================================================================ +<===> nth_child/same_arg_in_extender/input.scss +a { + b: selector-extend( + ":nth-child(2n + 1 of .c)", + ".c", + ":nth-child(2n + 1 of .d, .e)"); +} + +<===> nth_child/same_arg_in_extender/output.css +a { + b: :nth-child(2n+1 of .c, .d, .e); +} + +<===> +================================================================================ +<===> nth_child/different_arg_in_extender/input.scss +// This should produce `:nth-child(2n + 1 of .c, :nth-child(2n + 1 of .d, .e))`. +// See sass/sass#2828. +a { + b: selector-extend( + ":nth-child(2n + 1 of .c)", + ".c", + ":nth-child(2n + 2 of .d, .e)"); +} + +<===> nth_child/different_arg_in_extender/output.css +a { + b: :nth-child(2n+1 of .c); +} + +<===> +================================================================================ +<===> nth_last_child/options.yml +--- +:todo: +- sass/libsass#2961 + +<===> +================================================================================ +<===> nth_last_child/simple/input.scss +a {b: selector-extend(":nth-last-child(2n + 1 of .c)", ".c", ".d")} + +<===> nth_last_child/simple/output.css +a { + b: :nth-last-child(2n+1 of .c, .d); +} + +<===> +================================================================================ +<===> nth_last_child/list/input.scss +a {b: selector-extend(":nth-last-child(2n + 1 of .c)", ".c", ".d, .e")} + +<===> nth_last_child/list/output.css +a { + b: :nth-last-child(2n+1 of .c, .d, .e); +} + +<===> +================================================================================ +<===> nth_last_child/same_arg_in_extender/input.scss +a { + b: selector-extend( + ":nth-last-child(2n + 1 of .c)", + ".c", + ":nth-last-child(2n + 1 of .d, .e)"); +} + +<===> nth_last_child/same_arg_in_extender/output.css +a { + b: :nth-last-child(2n+1 of .c, .d, .e); +} + +<===> +================================================================================ +<===> nth_last_child/different_arg_in_extender/input.scss +// This should produce +// `:nth-last-child(2n + 1 of .c, :nth-last-child(2n + 1 of .d, .e))`. +// See sass/sass#2828. +a { + b: selector-extend( + ":nth-last-child(2n + 1 of .c)", + ".c", + ":nth-last-child(2n + 2 of .d, .e)"); +} + +<===> nth_last_child/different_arg_in_extender/output.css +a { + b: :nth-last-child(2n+1 of .c); +} + +<===> +================================================================================ +<===> prefixed/simple/input.scss +a {b: selector-extend(":-ms-matches(.c)", ".c", ".d")} + +<===> prefixed/simple/output.css +a { + b: :-ms-matches(.c, .d); +} + +<===> +================================================================================ +<===> prefixed/list/input.scss +a {b: selector-extend(":-ms-matches(.c)", ".c", ".d, .e")} + +<===> prefixed/list/output.css +a { + b: :-ms-matches(.c, .d, .e); +} + +<===> +================================================================================ +<===> prefixed/same_prefix_in_extender/options.yml +--- +:todo: +- sass/libsass#3069 + +<===> prefixed/same_prefix_in_extender/input.scss +a {b: selector-extend(":-ms-matches(.c)", ".c", ":-ms-matches(.d, .e)")} + +<===> prefixed/same_prefix_in_extender/output.css +a { + b: :-ms-matches(.c, .d, .e); +} + +<===> +================================================================================ +<===> prefixed/different_prefix_in_extender/input.scss +a {b: selector-extend(":-ms-matches(.c)", ".c", ":-moz-matches(.d, .e)")} + +<===> prefixed/different_prefix_in_extender/output.css +a { + b: :-ms-matches(.c); +} + +<===> prefixed/different_prefix_in_extender/output-libsass.css +a { + b: :-ms-matches(.c, :-moz-matches(.d, .e)); +} diff --git a/spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx b/spec/core_functions/selector/extend/simple/pseudo/selector/match.hrx similarity index 94% rename from spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx rename to spec/core_functions/selector/extend/simple/pseudo/selector/match.hrx index 972a050f25..9f5f8d31d1 100644 --- a/spec/core_functions/selector/extend/simple/pseudo/selector_arg.hrx +++ b/spec/core_functions/selector/extend/simple/pseudo/selector/match.hrx @@ -1,7 +1,8 @@ <===> README.md -For the purposes of figuring out which simple selectors to extend, -`selector-extend` doesn't care about the semantics of particular pseudo -selectors. All it looks at is whether they're structurally equal. +Specs for which selectors with argument lists count as "the same" when +determining which simple selectors to extend. `selector-extend` doesn't care +about the semantics of particular pseudo selectors. All it looks at is whether +they're structurally equal. <===> ================================================================================ diff --git a/spec/core_functions/selector/extend/simple/pseudo/selector/non_idempotent.hrx b/spec/core_functions/selector/extend/simple/pseudo/selector/non_idempotent.hrx new file mode 100644 index 0000000000..9ccaf634db --- /dev/null +++ b/spec/core_functions/selector/extend/simple/pseudo/selector/non_idempotent.hrx @@ -0,0 +1,123 @@ +<===> README.md +Nesting one of these selector pseudoclasses within an identical selector +pseudoclass changes its semantics, so Sass never flattens these nestings. + +<===> +================================================================================ +<===> has/simple/input.scss +a {b: selector-extend(":has(.c)", ".c", ".d")} + +<===> has/simple/output.css +a { + b: :has(.c, .d); +} + +<===> +================================================================================ +<===> has/list/input.scss +a {b: selector-extend(":has(.c)", ".c", ".d, .e")} + +<===> has/list/output.css +a { + b: :has(.c, .d, .e); +} + +<===> +================================================================================ +<===> has/has_in_extender/input.scss +a {b: selector-extend(":has(.c)", ".c", ":has(.d)")} + +<===> has/has_in_extender/output.css +a { + b: :has(.c, :has(.d)); +} + +<===> +================================================================================ +<===> host/simple/input.scss +a {b: selector-extend(":host(.c)", ".c", ".d")} + +<===> host/simple/output.css +a { + b: :host(.c, .d); +} + +<===> +================================================================================ +<===> host/list/input.scss +a {b: selector-extend(":host(.c)", ".c", ".d, .e")} + +<===> host/list/output.css +a { + b: :host(.c, .d, .e); +} + +<===> +================================================================================ +<===> host/host_in_extender/input.scss +a {b: selector-extend(":host(.c)", ".c", ":host(.d)")} + +<===> host/host_in_extender/output.css +a { + b: :host(.c, :host(.d)); +} + +<===> +================================================================================ +<===> host_context/simple/input.scss +a {b: selector-extend(":host-context(.c)", ".c", ".d")} + +<===> host_context/simple/output.css +a { + b: :host-context(.c, .d); +} + +<===> +================================================================================ +<===> host_context/list/input.scss +a {b: selector-extend(":host-context(.c)", ".c", ".d, .e")} + +<===> host_context/list/output.css +a { + b: :host-context(.c, .d, .e); +} + +<===> +================================================================================ +<===> host_context/host_context_in_extender/input.scss +a {b: selector-extend(":host-context(.c)", ".c", ":host-context(.d)")} + +<===> host_context/host_context_in_extender/output.css +a { + b: :host-context(.c, :host-context(.d)); +} + +<===> +================================================================================ +<===> slotted/simple/input.scss +a {b: selector-extend("::slotted(.c)", ".c", ".d")} + +<===> slotted/simple/output.css +a { + b: ::slotted(.c, .d); +} + +<===> +================================================================================ +<===> slotted/list/input.scss +a {b: selector-extend("::slotted(.c)", ".c", ".d, .e")} + +<===> slotted/list/output.css +a { + b: ::slotted(.c, .d, .e); +} + +<===> +================================================================================ +<===> slotted/slotted_in_extender/input.scss +a {b: selector-extend("::slotted(.c)", ".c", "::slotted(.d)")} + +<===> slotted/slotted_in_extender/output.css +a { + b: ::slotted(.c, ::slotted(.d)); +} From 2f6c362f06109de07560bf708c3ae11e3ab6cda4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 27 Feb 2020 18:41:37 -0800 Subject: [PATCH 7/8] Add specs for complex selectors --- .../selector/extend/complex.hrx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 spec/core_functions/selector/extend/complex.hrx diff --git a/spec/core_functions/selector/extend/complex.hrx b/spec/core_functions/selector/extend/complex.hrx new file mode 100644 index 0000000000..84e70d1687 --- /dev/null +++ b/spec/core_functions/selector/extend/complex.hrx @@ -0,0 +1,87 @@ +<===> parent/without_grandparent/simple/input.scss +a {b: selector-extend(".c .d", ".c", ".e")} + +<===> parent/without_grandparent/simple/output.css +a { + b: .c .d, .e .d; +} + +<===> +================================================================================ +<===> parent/without_grandparent/complex/input.scss +a {b: selector-extend(".c .d", ".c", ".e .f")} + +<===> parent/without_grandparent/complex/output.css +a { + b: .c .d, .e .f .d; +} + +<===> +================================================================================ +<===> parent/without_grandparent/list/input.scss +a {b: selector-extend(".c .d", ".c", ".e, .f")} + +<===> parent/without_grandparent/list/output.css +a { + b: .c .d, .e .d, .f .d; +} + +<===> +================================================================================ +<===> parent/with_grandparent/simple/input.scss +a {b: selector-extend(".c .d .e", ".d", ".f")} + +<===> parent/with_grandparent/simple/output.css +a { + b: .c .d .e, .c .f .e; +} + +<===> +================================================================================ +<===> parent/with_grandparent/complex/input.scss +a {b: selector-extend(".c .d .e", ".d", ".f .g")} + +<===> parent/with_grandparent/complex/output.css +a { + b: .c .d .e, .c .f .g .e, .f .c .g .e; +} + +<===> +================================================================================ +<===> parent/with_grandparent/list/input.scss +a {b: selector-extend(".c .d .e", ".d", ".f, .g")} + +<===> parent/with_grandparent/list/output.css +a { + b: .c .d .e, .c .f .e, .c .g .e; +} + +<===> +================================================================================ +<===> trailing_combinator/child/input.scss +a {b: selector-extend(".c .d", ".c", ".e >")} + +<===> trailing_combinator/child/output.css +a { + b: .c .d, .e > .d; +} + +<===> +================================================================================ +<===> trailing_combinator/sibling/input.scss +a {b: selector-extend(".c .d", ".c", ".e ~")} + +<===> trailing_combinator/sibling/output.css +a { + b: .c .d, .e ~ .d; +} + +<===> +================================================================================ +<===> trailing_combinator/next_sibling/input.scss +a {b: selector-extend(".c .d", ".c", ".e +")} + +<===> trailing_combinator/next_sibling/output.css +a { + b: .c .d, .e + .d; +} From 85494b1fc1dd06d8773d9007a3d7013d8c4cf5da Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 28 Feb 2020 15:23:59 -0800 Subject: [PATCH 8/8] Code review --- spec/core_functions/selector/extend/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/core_functions/selector/extend/README.md b/spec/core_functions/selector/extend/README.md index 91ec3ee655..1ea4c5c5ee 100644 --- a/spec/core_functions/selector/extend/README.md +++ b/spec/core_functions/selector/extend/README.md @@ -2,6 +2,6 @@ Much of the complexity in the algorithm for extend comes from determining which selectors are superselectors of which others and unifying two selectors, which are covered more explicitly by specs for the `is-superselector()` and `selector-unify()` functions, respectively. To avoid unnecessary duplication, -the specs for `selector-extend()` itself make an effort to thoroughly exercise -the superselector or unification logic, and instead focuses on behavior that's +the specs for `selector-extend()` itself don't thoroughly exercise the +superselector or unification logic, and instead focuses on behavior that's specific to the full extension process.