diff --git a/lib/LessParser.js b/lib/LessParser.js index 098cc4e..e0707db 100644 --- a/lib/LessParser.js +++ b/lib/LessParser.js @@ -177,6 +177,23 @@ module.exports = class LessParser extends Parser { } } + // Test for the mixin definition pattern + // .mixin-name() { + // .mixin-name(@param1; @param2) { + // .mixin-name(@param1; @param2) when (@condition) { + // .mixin-name(@param1; @param2) when (@condition) and (@condition) { + if (tokens[0][0] === 'word' && isMixinToken(tokens[0])) { + const bracketsIndex = tokens.findIndex((t) => t[0] === 'brackets'); + const firstParenIndex = tokens.findIndex((t) => t[0] === '('); + const spaceIndex = tokens.findIndex((t) => t[0] === 'space'); + + if ((spaceIndex === 1 && (firstParenIndex === 2 || bracketsIndex === 2)) || + firstParenIndex === 1 || bracketsIndex === 1) { + this.mixin(tokens); + return; + } + } + super.rule(tokens); // #123: add `extend` decorator to nodes diff --git a/test/parser/interpolation.test.js b/test/parser/interpolation.test.js index 5b9b0be..2bab4ab 100644 --- a/test/parser/interpolation.test.js +++ b/test/parser/interpolation.test.js @@ -24,7 +24,9 @@ test('parses mixin interpolation', (t) => { const less = '.browser-prefix(@prop, @args) {\n @{prop}: @args;\n}'; const root = parse(less); - t.is(root.first.selector, '.browser-prefix(@prop, @args)'); + t.is(root.first.type, 'atrule'); + t.is(root.first.name, 'browser-prefix'); + t.is(root.first.params, '(@prop, @args)'); t.is(root.first.first.prop, '@{prop}'); t.is(root.first.first.value, '@args'); }); @@ -77,7 +79,9 @@ test('parses escaping', (t) => { const root = parse(code); const { first } = root; - t.is(first.selector, '.m_transition (...)'); + t.is(first.type, 'atrule'); + t.is(first.name, 'm_transition'); + t.is(first.params, '(...)'); t.is(first.first.name, 'props'); t.is(first.first.value, '~`"@{arguments}".replace(/[[]]/g, \'\')`'); t.is(root.nodes[1].first.selector, '& ~ .stock-bar__content .stock-bar__control_pause'); diff --git a/test/parser/mixins-definition.test.js b/test/parser/mixins-definition.test.js new file mode 100644 index 0000000..6269581 --- /dev/null +++ b/test/parser/mixins-definition.test.js @@ -0,0 +1,58 @@ +const test = require('ava'); + +const { parse } = require('../../lib'); + +test('with empty params', async (t) => { + const less = '.foo() { color: red; }'; + const { first } = parse(less); + + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, '()'); + + console.log(first); +}); + +test('with one param', async (t) => { + const less = '.foo(@_color: red) { color: @_color; }'; + const { first } = parse(less); + + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, '(@_color: red)'); +}); + +test('with two params', async (t) => { + const less = '.foo(@_color: red; @_width: 100px) { color: @_color; width: @_width; }'; + const { first } = parse(less); + + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, '(@_color: red; @_width: 100px)'); +}); + +test('with params and when condition', async (t) => { + const less = '.foo(@_color: red; @_width: 100px) when (@mode = huge) { color: @_color; width: @_width; }'; + const { first } = parse(less); + + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, '(@_color: red; @_width: 100px) when (@mode = huge)'); +}); + +test('with params and 2x when condition', async (t) => { + const less = '.foo(@_color: red; @_width: 100px) when (@mode = huge) and (@mode = small) { color: @_color; width: @_width; }'; + const { first } = parse(less); + + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, '(@_color: red; @_width: 100px) when (@mode = huge) and (@mode = small)'); +}); + +test('avoids parsing pseudos as mixins', async (t) => { + const less = '.foo:is(input) { color: red; }'; + const { first } = parse(less); + + t.is(first.type, 'rule'); + t.is(first.selector, '.foo:is(input)'); +}); diff --git a/test/parser/mixins.test.js b/test/parser/mixins.test.js index c218ddb..5bfe809 100644 --- a/test/parser/mixins.test.js +++ b/test/parser/mixins.test.js @@ -9,8 +9,9 @@ test('basic', (t) => { const root = parse(less); const { first } = root; - t.is(first.type, 'rule'); - t.is(first.selector, selector); + t.is(first.type, 'atrule'); + t.is(first.name, 'foo'); + t.is(first.params, params); t.is(first.first.prop, 'border'); t.is(first.first.value, 'baz'); t.is(nodeToString(root), less); @@ -128,8 +129,14 @@ test('guarded namespaces', (t) => { const root = parse(less); const { first } = root; - t.is(first.first.selector, '.mixin()'); - t.is(first.next().first.selector, '.mixin() when (@mode=huge)'); + t.is(first.first.type, 'atrule'); + t.is(first.first.name, 'mixin'); + t.is(first.first.params, '()'); + + const next = first.next(); + t.is(next.first.type, 'atrule'); + t.is(next.first.name, 'mixin'); + t.is(next.first.params, '() when (@mode=huge)'); t.is(nodeToString(root), less); });