Skip to content

Commit

Permalink
Merge pull request #2453 from sass/return
Browse files Browse the repository at this point in the history
Add sass-parser support for `@return` and `@mixin`
  • Loading branch information
nex3 authored Dec 6, 2024
2 parents a74f9c3 + 0918ade commit 1536dc0
Show file tree
Hide file tree
Showing 26 changed files with 1,135 additions and 220 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
70 changes: 13 additions & 57 deletions pkg/sass-parser/lib/src/expression/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,41 +25,14 @@ describe('a string expression', () => {

describeNode('parsed', () => utils.parseExpression('"foo"'));

describe('constructed manually', () => {
describeNode(
'with explicit text',
() =>
new StringExpression({
quotes: true,
text: new Interpolation({nodes: ['foo']}),
}),
);

describeNode(
'with string text',
() =>
new StringExpression({
quotes: true,
text: 'foo',
}),
);
});

describe('constructed from ExpressionProps', () => {
describeNode('with explicit text', () =>
utils.fromExpressionProps({
quotes: true,
text: new Interpolation({nodes: ['foo']}),
}),
);

describeNode('with string text', () =>
utils.fromExpressionProps({
quotes: true,
text: 'foo',
}),
);
});
describeNode(
'constructed manually',
() => new StringExpression({quotes: true, text: 'foo'}),
);

describeNode('constructed from ExpressionProps', () =>
utils.fromExpressionProps({quotes: true, text: 'foo'}),
);
});

describe('unquoted', () => {
Expand All @@ -81,29 +54,14 @@ describe('a string expression', () => {
describeNode('parsed', () => utils.parseExpression('foo'));

describe('constructed manually', () => {
describeNode(
'with explicit text',
() =>
new StringExpression({
text: new Interpolation({nodes: ['foo']}),
}),
);

describeNode(
'with explicit quotes',
() =>
new StringExpression({
quotes: false,
text: 'foo',
}),
() => new StringExpression({quotes: false, text: 'foo'}),
);

describeNode(
'with string text',
() =>
new StringExpression({
text: 'foo',
}),
'with default quotes',
() => new StringExpression({text: 'foo'}),
);
});

Expand All @@ -122,9 +80,7 @@ describe('a string expression', () => {
);

describeNode('with string text', () =>
utils.fromExpressionProps({
text: 'foo',
}),
utils.fromExpressionProps({text: 'foo'}),
);
});
});
Expand All @@ -145,7 +101,7 @@ describe('a string expression', () => {
});

it('assigns text explicitly', () => {
const text = new Interpolation({nodes: ['zip']});
const text = new Interpolation('zip');
node.text = text;
expect(node.text).toBe(text);
expect(node).toHaveInterpolation('text', 'zip');
Expand Down
9 changes: 5 additions & 4 deletions pkg/sass-parser/lib/src/expression/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as postcss from 'postcss';

import {Interpolation} from '../interpolation';
import {Interpolation, InterpolationProps} from '../interpolation';
import {LazySource} from '../lazy-source';
import type * as sassInternal from '../sass-internal';
import * as utils from '../utils';
Expand All @@ -16,7 +16,7 @@ import {Expression} from '.';
* @category Expression
*/
export interface StringExpressionProps {
text: Interpolation | string;
text: Interpolation | InterpolationProps;
quotes?: boolean;
raws?: StringExpressionRaws;
}
Expand Down Expand Up @@ -48,10 +48,11 @@ export class StringExpression extends Expression {
get text(): Interpolation {
return this._text;
}
set text(text: Interpolation | string) {
set text(value: Interpolation | InterpolationProps) {
// TODO - postcss/postcss#1957: Mark this as dirty
if (this._text) this._text.parent = undefined;
if (typeof text === 'string') text = new Interpolation({nodes: [text]});
const text =
value instanceof Interpolation ? value : new Interpolation(value);
text.parent = this;
this._text = text;
}
Expand Down
90 changes: 78 additions & 12 deletions pkg/sass-parser/lib/src/interpolation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,32 @@ describe('an interpolation', () => {
() => (css.parse('@foo').nodes[0] as GenericAtRule).nameInterpolation,
);

describeNode(
'constructed manually',
() => new Interpolation({nodes: ['foo']}),
);
describe('constructed manually', () => {
describeNode('with an object', () => new Interpolation({nodes: ['foo']}));

describeNode('with an array', () => new Interpolation(['foo']));

describeNode('with a string', () => new Interpolation('foo'));
});

describe('constructed from properties', () => {
describeNode(
'with an object',
() =>
new GenericAtRule({nameInterpolation: {nodes: ['foo']}})
.nameInterpolation,
);

describeNode(
'with an array',
() => new GenericAtRule({nameInterpolation: ['foo']}).nameInterpolation,
);

describeNode(
'with a string',
() => new GenericAtRule({nameInterpolation: 'foo'}).nameInterpolation,
);
});
});

describe('with only an expression', () => {
Expand Down Expand Up @@ -102,10 +124,30 @@ describe('an interpolation', () => {
() => (scss.parse('@#{foo}').nodes[0] as GenericAtRule).nameInterpolation,
);

describeNode(
'constructed manually',
() => new Interpolation({nodes: [{text: 'foo'}]}),
);
describe('constructed manually', () => {
describeNode(
'with an object',
() => new Interpolation({nodes: [{text: 'foo'}]}),
);

describeNode('with an array', () => new Interpolation([{text: 'foo'}]));
});

describe('constructed from properties', () => {
describeNode(
'with an object',
() =>
new GenericAtRule({nameInterpolation: {nodes: [{text: 'foo'}]}})
.nameInterpolation,
);

describeNode(
'with an array',
() =>
new GenericAtRule({nameInterpolation: [{text: 'foo'}]})
.nameInterpolation,
);
});
});

describe('with mixed text and expressions', () => {
Expand Down Expand Up @@ -139,10 +181,34 @@ describe('an interpolation', () => {
.nameInterpolation,
);

describeNode(
'constructed manually',
() => new Interpolation({nodes: ['foo', {text: 'bar'}, 'baz']}),
);
describe('constructed manually', () => {
describeNode(
'with an object',
() => new Interpolation({nodes: ['foo', {text: 'bar'}, 'baz']}),
);

describeNode(
'with an array',
() => new Interpolation(['foo', {text: 'bar'}, 'baz']),
);
});

describe('constructed from properties', () => {
describeNode(
'with an object',
() =>
new GenericAtRule({
nameInterpolation: {nodes: ['foo', {text: 'bar'}, 'baz']},
}).nameInterpolation,
);

describeNode(
'with an array',
() =>
new GenericAtRule({nameInterpolation: ['foo', {text: 'bar'}, 'baz']})
.nameInterpolation,
);
});
});

describe('can add', () => {
Expand Down
29 changes: 24 additions & 5 deletions pkg/sass-parser/lib/src/interpolation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,28 @@ export type NewNodeForInterpolation =
| undefined;

/**
* The initializer properties for {@link Interpolation}
* The initializer properties for {@link Interpolation} passed as an options
* object.
*
* @category Expression
*/
export interface InterpolationProps {
export interface InterpolationObjectProps {
nodes: ReadonlyArray<NewNodeForInterpolation>;
raws?: InterpolationRaws;
}

/**
* The initializer properties for {@link Interpolation} passed.
*
* A plain string is interpreted as a plain-text interpolation.
*
* @category Expression
*/
export type InterpolationProps =
| InterpolationObjectProps
| ReadonlyArray<NewNodeForInterpolation>
| string;

/**
* Raws indicating how to precisely serialize an {@link Interpolation} node.
*
Expand Down Expand Up @@ -131,8 +144,14 @@ export class Interpolation
constructor(defaults?: InterpolationProps);
/** @hidden */
constructor(_: undefined, inner: sassInternal.Interpolation);
constructor(defaults?: object, inner?: sassInternal.Interpolation) {
super(defaults);
constructor(defaults?: object | string, inner?: sassInternal.Interpolation) {
super(
typeof defaults === 'string'
? {nodes: [defaults]}
: Array.isArray(defaults)
? {nodes: defaults}
: defaults,
);
if (inner) {
this.source = new LazySource(inner);
// TODO: set lazy raws here to use when stringifying
Expand All @@ -146,7 +165,7 @@ export class Interpolation
if (this._nodes === undefined) this._nodes = [];
}

clone(overrides?: Partial<InterpolationProps>): this {
clone(overrides?: Partial<InterpolationObjectProps>): this {
return utils.cloneNode(this, overrides, ['nodes', 'raws']);
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/sass-parser/lib/src/parameter-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type NewParameters =
* @category Statement
*/
export interface ParameterListObjectProps {
nodes?: ReadonlyArray<ParameterProps>;
nodes?: ReadonlyArray<NewParameters>;
restParameter?: string;
raws?: ParameterListRaws;
}
Expand All @@ -43,7 +43,7 @@ export interface ParameterListObjectProps {
*/
export type ParameterListProps =
| ParameterListObjectProps
| ReadonlyArray<ParameterProps>;
| ReadonlyArray<NewParameters>;

/**
* Raws indicating how to precisely serialize a {@link ParameterList} node.
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
Loading

0 comments on commit 1536dc0

Please sign in to comment.