Skip to content

Commit

Permalink
Add sass-parser support for @return and @mixin
Browse files Browse the repository at this point in the history
  • Loading branch information
nex3 committed Dec 4, 2024
1 parent 93e6c2e commit 0918ade
Show file tree
Hide file tree
Showing 18 changed files with 978 additions and 66 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.82.1-dev

* No user-visible changes.

## 1.82.0

### Command-Line Interface
Expand Down
3 changes: 2 additions & 1 deletion lib/src/js/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ void _updateAstPrototypes() {
.defineGetter('arguments', (ArgumentDeclaration self) => self.arguments);
var function = FunctionRule('a', arguments, [], bogusSpan);
getJSClass(function)
.defineGetter('arguments', (FunctionRule self) => self.arguments);
.superclass
.defineGetter('arguments', (CallableDeclaration self) => self.arguments);

_addSupportsConditionToInterpolation();

Expand Down
6 changes: 6 additions & 0 deletions pkg/sass-parser/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.4.8-dev

Add support for parsing the `@mixin` rule.

Add support for parsing the `@return` rule.

## 0.4.7

* No user-visible changes.
Expand Down
10 changes: 10 additions & 0 deletions pkg/sass-parser/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ export {
GenericAtRuleProps,
GenericAtRuleRaws,
} from './src/statement/generic-at-rule';
export {
MixinRule,
MixinRuleProps,
MixinRuleRaws,
} from './src/statement/mixin-rule';
export {
ReturnRule,
ReturnRuleProps,
ReturnRuleRaws,
} from './src/statement/return-rule';
export {Root, RootProps, RootRaws} from './src/statement/root';
export {Rule, RuleProps, RuleRaws} from './src/statement/rule';
export {
Expand Down
13 changes: 13 additions & 0 deletions pkg/sass-parser/lib/src/sass-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ declare namespace SassInternal {
readonly query: Interpolation;
}

class MixinRule extends ParentStatement<Statement[]> {
readonly name: string;
readonly arguments: ArgumentDeclaration;
}

class ReturnRule extends Statement {
readonly expression: Expression;
}

class SilentComment extends Statement {
readonly text: string;
}
Expand Down Expand Up @@ -285,6 +294,8 @@ export type ForwardRule = SassInternal.ForwardRule;
export type FunctionRule = SassInternal.FunctionRule;
export type LoudComment = SassInternal.LoudComment;
export type MediaRule = SassInternal.MediaRule;
export type MixinRule = SassInternal.MixinRule;
export type ReturnRule = SassInternal.ReturnRule;
export type SilentComment = SassInternal.SilentComment;
export type Stylesheet = SassInternal.Stylesheet;
export type StyleRule = SassInternal.StyleRule;
Expand Down Expand Up @@ -315,6 +326,8 @@ export interface StatementVisitorObject<T> {
visitFunctionRule(node: FunctionRule): T;
visitLoudComment(node: LoudComment): T;
visitMediaRule(node: MediaRule): T;
visitMixinRule(node: MixinRule): T;
visitReturnRule(node: ReturnRule): T;
visitSilentComment(node: SilentComment): T;
visitStyleRule(node: StyleRule): T;
visitSupportsRule(node: SupportsRule): T;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`a @mixin rule toJSON 1`] = `
{
"inputs": [
{
"css": "@mixin foo($bar) {}",
"hasBOM": false,
"id": "<input css _____>",
},
],
"mixinName": "foo",
"name": "mixin",
"nodes": [],
"parameters": <($bar)>,
"raws": {},
"sassType": "mixin-rule",
"source": <1:1-1:20 in 0>,
"type": "atrule",
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`a @return rule toJSON 1`] = `
{
"inputs": [
{
"css": "@function x() {@return foo}",
"hasBOM": false,
"id": "<input css _____>",
},
],
"name": "return",
"params": "foo",
"raws": {},
"returnExpression": <foo>,
"sassType": "return-rule",
"source": <1:16-1:27 in 0>,
"type": "atrule",
}
`;
117 changes: 62 additions & 55 deletions pkg/sass-parser/lib/src/statement/function-rule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import {FunctionRule, ParameterList, sass, scss} from '../..';
import {FunctionRule, ParameterList, ReturnRule, sass, scss} from '../..';
import * as utils from '../../../test/utils';

describe('a @function rule', () => {
Expand Down Expand Up @@ -49,60 +49,67 @@ describe('a @function rule', () => {
);
});

// TODO(nweiz): Enable this when we parse ReturnRule.
//
// describe('with a child', () => {
// function describeNode(description: string, create: () => FunctionRule): void {
// describe(description, () => {
// beforeEach(() => void (node = create()));
//
// it('has a name', () => expect(node.name.toString()).toBe('function'));
//
// it('has a function name', () => expect(node.functionName.toString()).toBe('foo'));
//
// it('has a parameter', () =>
// expect(node.parameters.nodes[0].name).toEqual('bar'));
//
// it('has matching params', () =>
// expect(node.params).toBe('foo($bar)'));
//
// it('has a child node', () => {
// expect(node.nodes).toHaveLength(1);
// expect(node.nodes[0]).toBeInstanceOf(ReturnRule);
// expect(node.nodes[0]).toHaveStringExpression('returnExpression', 'baz');
// });
// });
// }
//
// describeNode(
// 'parsed as SCSS',
// () => scss.parse('@function foo($bar) {@return "baz"}').nodes[0] as FunctionRule,
// );
//
// describeNode(
// 'parsed as Sass',
// () =>
// sass.parse('@function foo($bar)\n @return "baz"').nodes[0] as FunctionRule,
// );
//
// describeNode(
// 'constructed manually',
// () =>
// new FunctionRule({
// name: 'foo',
// parameters: ['bar'],
// nodes: [{returnExpression: 'child'}],
// }),
// );
//
// describeNode('constructed from ChildProps', () =>
// utils.fromChildProps({
// name: 'foo',
// parameters: ['bar'],
// nodes: [{returnExpression: 'child'}],
// }),
// );
// });
describe('with a child', () => {
function describeNode(
description: string,
create: () => FunctionRule,
): void {
describe(description, () => {
beforeEach(() => void (node = create()));

it('has a name', () => expect(node.name.toString()).toBe('function'));

it('has a function name', () =>
expect(node.functionName.toString()).toBe('foo'));

it('has a parameter', () =>
expect(node.parameters.nodes[0].name).toEqual('bar'));

it('has matching params', () => expect(node.params).toBe('foo($bar)'));

it('has a child node', () => {
expect(node.nodes).toHaveLength(1);
expect(node.nodes[0]).toBeInstanceOf(ReturnRule);
expect(node.nodes[0]).toHaveStringExpression(
'returnExpression',
'baz',
);
});
});
}

describeNode(
'parsed as SCSS',
() =>
scss.parse('@function foo($bar) {@return "baz"}')
.nodes[0] as FunctionRule,
);

describeNode(
'parsed as Sass',
() =>
sass.parse('@function foo($bar)\n @return "baz"')
.nodes[0] as FunctionRule,
);

describeNode(
'constructed manually',
() =>
new FunctionRule({
functionName: 'foo',
parameters: ['bar'],
nodes: [{returnExpression: {text: 'baz'}}],
}),
);

describeNode('constructed from ChildProps', () =>
utils.fromChildProps({
functionName: 'foo',
parameters: ['bar'],
nodes: [{returnExpression: {text: 'baz'}}],
}),
);
});

describe('throws an error when assigned a new', () => {
beforeEach(
Expand Down
26 changes: 20 additions & 6 deletions pkg/sass-parser/lib/src/statement/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {ErrorRule, ErrorRuleProps} from './error-rule';
import {ForRule, ForRuleProps} from './for-rule';
import {ForwardRule, ForwardRuleProps} from './forward-rule';
import {FunctionRule, FunctionRuleProps} from './function-rule';
import {MixinRule, MixinRuleProps} from './mixin-rule';
import {ReturnRule, ReturnRuleProps} from './return-rule';
import {Root} from './root';
import {Rule, RuleProps} from './rule';
import {UseRule, UseRuleProps} from './use-rule';
Expand Down Expand Up @@ -55,12 +57,14 @@ export type StatementType =
| 'comment'
| 'debug-rule'
| 'each-rule'
| 'error-rule'
| 'for-rule'
| 'forward-rule'
| 'function-rule'
| 'error-rule'
| 'use-rule'
| 'mixin-rule'
| 'return-rule'
| 'sass-comment'
| 'use-rule'
| 'variable-declaration'
| 'warn-rule'
| 'while-rule';
Expand All @@ -78,6 +82,8 @@ export type AtRule =
| ForwardRule
| FunctionRule
| GenericAtRule
| MixinRule
| ReturnRule
| UseRule
| WarnRule
| WhileRule;
Expand Down Expand Up @@ -115,6 +121,8 @@ export type ChildProps =
| ForwardRuleProps
| FunctionRuleProps
| GenericAtRuleProps
| MixinRuleProps
| ReturnRuleProps
| RuleProps
| SassCommentChildProps
| UseRuleProps
Expand Down Expand Up @@ -195,6 +203,8 @@ const visitor = sassInternal.createStatementVisitor<Statement>({
appendInternalChildren(rule, inner.children);
return rule;
},
visitMixinRule: inner => new MixinRule(undefined, inner),
visitReturnRule: inner => new ReturnRule(undefined, inner),
visitSilentComment: inner => new SassComment(undefined, inner),
visitStyleRule: inner => new Rule(undefined, inner),
visitSupportsRule: inner => {
Expand Down Expand Up @@ -319,18 +329,22 @@ export function normalize(
result.push(new DebugRule(node));
} else if ('eachExpression' in node) {
result.push(new EachRule(node));
} else if ('errorExpression' in node) {
result.push(new ErrorRule(node));
} else if ('fromExpression' in node) {
result.push(new ForRule(node));
} else if ('forwardUrl' in node) {
result.push(new ForwardRule(node));
} else if ('functionName' in node) {
result.push(new FunctionRule(node));
} else if ('errorExpression' in node) {
result.push(new ErrorRule(node));
} else if ('text' in node || 'textInterpolation' in node) {
result.push(new CssComment(node as CssCommentProps));
} else if ('mixinName' in node) {
result.push(new MixinRule(node));
} else if ('returnExpression' in node) {
result.push(new ReturnRule(node));
} else if ('silentText' in node) {
result.push(new SassComment(node));
} else if ('text' in node || 'textInterpolation' in node) {
result.push(new CssComment(node as CssCommentProps));
} else if ('useUrl' in node) {
result.push(new UseRule(node));
} else if ('variableName' in node) {
Expand Down
Loading

0 comments on commit 0918ade

Please sign in to comment.