Skip to content

Commit 02fff80

Browse files
authored
Merge pull request #2439 from sass/function
Add sass-parser support for `@function`
2 parents 6939faa + b10ca79 commit 02fff80

25 files changed

+2735
-164
lines changed

lib/src/js/parser.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ void _updateAstPrototypes() {
8888
'accept',
8989
(Expression self, ExpressionVisitor<Object?> visitor) =>
9090
self.accept(visitor));
91+
var arguments = ArgumentDeclaration([], bogusSpan);
92+
getJSClass(arguments)
93+
.defineGetter('arguments', (ArgumentDeclaration self) => self.arguments);
94+
var function = FunctionRule('a', arguments, [], bogusSpan);
95+
getJSClass(function)
96+
.defineGetter('arguments', (FunctionRule self) => self.arguments);
9197

9298
_addSupportsConditionToInterpolation();
9399

pkg/sass-parser/lib/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export {
2020
ConfiguredVariableProps,
2121
ConfiguredVariableRaws,
2222
} from './src/configured-variable';
23+
export {Container} from './src/container';
2324
export {AnyNode, Node, NodeProps, NodeType} from './src/node';
2425
export {RawWithValue} from './src/raw-with-value';
2526
export {
@@ -55,6 +56,20 @@ export {
5556
InterpolationRaws,
5657
NewNodeForInterpolation,
5758
} from './src/interpolation';
59+
export {
60+
NewParameters,
61+
ParameterListObjectProps,
62+
ParameterListProps,
63+
ParameterListRaws,
64+
ParameterList,
65+
} from './src/parameter-list';
66+
export {
67+
ParameterObjectProps,
68+
ParameterRaws,
69+
ParameterExpressionProps,
70+
ParameterProps,
71+
Parameter,
72+
} from './src/parameter';
5873
export {
5974
CssComment,
6075
CssCommentProps,
@@ -79,6 +94,11 @@ export {
7994
ForwardRuleProps,
8095
ForwardRuleRaws,
8196
} from './src/statement/forward-rule';
97+
export {
98+
FunctionRuleRaws,
99+
FunctionRuleProps,
100+
FunctionRule,
101+
} from './src/statement/function-rule';
82102
export {
83103
GenericAtRule,
84104
GenericAtRuleProps,

pkg/sass-parser/lib/src/__snapshots__/configured-variable.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ exports[`a configured variable toJSON 1`] = `
1111
"id": "<input css _____>",
1212
},
1313
],
14+
"name": "baz",
1415
"raws": {},
1516
"sassType": "configured-variable",
1617
"source": <1:18-1:29 in 0>,
17-
"variableName": "baz",
1818
}
1919
`;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`a parameter list toJSON 1`] = `
4+
{
5+
"inputs": [
6+
{
7+
"css": "@function x($foo, $bar...) {}",
8+
"hasBOM": false,
9+
"id": "<input css _____>",
10+
},
11+
],
12+
"nodes": [
13+
<$foo>,
14+
],
15+
"raws": {},
16+
"restParameter": "bar",
17+
"sassType": "parameter-list",
18+
"source": <1:12-1:27 in 0>,
19+
}
20+
`;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`a parameter toJSON with a default 1`] = `
4+
{
5+
"defaultValue": <"qux">,
6+
"inputs": [
7+
{
8+
"css": "@function x($baz: "qux") {}",
9+
"hasBOM": false,
10+
"id": "<input css _____>",
11+
},
12+
],
13+
"name": "baz",
14+
"raws": {},
15+
"sassType": "parameter",
16+
"source": <1:13-1:24 in 0>,
17+
}
18+
`;
19+
20+
exports[`a parameter toJSON with no default 1`] = `
21+
{
22+
"inputs": [
23+
{
24+
"css": "@function x($baz) {}",
25+
"hasBOM": false,
26+
"id": "<input css _____>",
27+
},
28+
],
29+
"name": "baz",
30+
"raws": {},
31+
"sassType": "parameter",
32+
"source": <1:13-1:17 in 0>,
33+
}
34+
`;

pkg/sass-parser/lib/src/configuration.test.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe('a configuration map', () => {
7676
it('contains the variable', () => {
7777
expect(node.size).toBe(1);
7878
const variable = [...node.variables()][0];
79-
expect(variable.variableName).toEqual('bar');
79+
expect(variable.name).toEqual('bar');
8080
expect(variable).toHaveStringExpression('expression', 'baz');
8181
});
8282
});
@@ -101,9 +101,7 @@ describe('a configuration map', () => {
101101
'variables array',
102102
() =>
103103
new Configuration({
104-
variables: [
105-
{variableName: 'bar', expression: {text: 'baz', quotes: true}},
106-
],
104+
variables: [{name: 'bar', expression: {text: 'baz', quotes: true}}],
107105
}),
108106
);
109107

@@ -127,7 +125,7 @@ describe('a configuration map', () => {
127125
describe('add()', () => {
128126
test('with a ConfiguredVariable', () => {
129127
const variable = new ConfiguredVariable({
130-
variableName: 'foo',
128+
name: 'foo',
131129
expression: {text: 'bar', quotes: true},
132130
});
133131
expect(node.add(variable)).toBe(node);
@@ -137,29 +135,29 @@ describe('a configuration map', () => {
137135
});
138136

139137
test('with a ConfiguredVariableProps', () => {
140-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
138+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
141139
expect(node.size).toBe(1);
142140
const variable = node.get('foo');
143-
expect(variable?.variableName).toBe('foo');
141+
expect(variable?.name).toBe('foo');
144142
expect(variable).toHaveStringExpression('expression', 'bar');
145143
expect(variable?.parent).toBe(node);
146144
});
147145

148146
test('overwrites on old variable', () => {
149-
node.add({variableName: 'foo', expression: {text: 'old', quotes: true}});
147+
node.add({name: 'foo', expression: {text: 'old', quotes: true}});
150148
const old = node.get('foo');
151149
expect(old?.parent).toBe(node);
152150

153-
node.add({variableName: 'foo', expression: {text: 'new', quotes: true}});
151+
node.add({name: 'foo', expression: {text: 'new', quotes: true}});
154152
expect(node.size).toBe(1);
155153
expect(old?.parent).toBeUndefined();
156154
expect(node.get('foo')).toHaveStringExpression('expression', 'new');
157155
});
158156
});
159157

160158
test('clear() removes all variables', () => {
161-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
162-
node.add({variableName: 'baz', expression: {text: 'bang', quotes: true}});
159+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
160+
node.add({name: 'baz', expression: {text: 'bang', quotes: true}});
163161
const foo = node.get('foo');
164162
const bar = node.get('bar');
165163
node.clear();
@@ -172,8 +170,8 @@ describe('a configuration map', () => {
172170

173171
describe('delete()', () => {
174172
beforeEach(() => {
175-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
176-
node.add({variableName: 'baz', expression: {text: 'bang', quotes: true}});
173+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
174+
node.add({name: 'baz', expression: {text: 'bang', quotes: true}});
177175
});
178176

179177
test('removes a matching variable', () => {
@@ -192,12 +190,12 @@ describe('a configuration map', () => {
192190

193191
describe('get()', () => {
194192
beforeEach(() => {
195-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
193+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
196194
});
197195

198196
test('returns a variable in the configuration', () => {
199197
const variable = node.get('foo');
200-
expect(variable?.variableName).toBe('foo');
198+
expect(variable?.name).toBe('foo');
201199
expect(variable).toHaveStringExpression('expression', 'bar');
202200
});
203201

@@ -208,7 +206,7 @@ describe('a configuration map', () => {
208206

209207
describe('has()', () => {
210208
beforeEach(() => {
211-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
209+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
212210
});
213211

214212
test('returns true for a variable in the configuration', () =>
@@ -220,7 +218,7 @@ describe('a configuration map', () => {
220218

221219
describe('set()', () => {
222220
beforeEach(() => {
223-
node.add({variableName: 'foo', expression: {text: 'bar', quotes: true}});
221+
node.add({name: 'foo', expression: {text: 'bar', quotes: true}});
224222
});
225223

226224
describe('adds a new variable', () => {
@@ -233,7 +231,7 @@ describe('a configuration map', () => {
233231
expect(node.size).toBe(2);
234232
const variable = node.get('baz');
235233
expect(variable?.parent).toBe(node);
236-
expect(variable?.variableName).toBe('baz');
234+
expect(variable?.name).toBe('baz');
237235
expect(variable).toHaveStringExpression('expression', 'bang');
238236
});
239237
}
@@ -285,15 +283,15 @@ describe('a configuration map', () => {
285283
}).toString(),
286284
).toBe('($foo: "bar", $baz: "bang",)'));
287285

288-
it('with comma: true and afterValue', () =>
286+
it('with comma: true and after', () =>
289287
expect(
290288
new Configuration({
291289
raws: {comma: true},
292290
variables: {
293291
foo: {text: 'bar', quotes: true},
294292
baz: {
295293
expression: {text: 'bang', quotes: true},
296-
raws: {afterValue: '/**/'},
294+
raws: {after: '/**/'},
297295
},
298296
},
299297
}).toString(),
@@ -310,28 +308,28 @@ describe('a configuration map', () => {
310308
}).toString(),
311309
).toBe('($foo: "bar", $baz: "bang"/**/)'));
312310

313-
it('with after and afterValue', () =>
311+
it('with after and after', () =>
314312
expect(
315313
new Configuration({
316314
raws: {after: '/**/'},
317315
variables: {
318316
foo: {text: 'bar', quotes: true},
319317
baz: {
320318
expression: {text: 'bang', quotes: true},
321-
raws: {afterValue: ' '},
319+
raws: {after: ' '},
322320
},
323321
},
324322
}).toString(),
325323
).toBe('($foo: "bar", $baz: "bang" /**/)'));
326324

327-
it('with afterValue and a guard', () =>
325+
it('with after and a guard', () =>
328326
expect(
329327
new Configuration({
330328
variables: {
331329
foo: {text: 'bar', quotes: true},
332330
baz: {
333331
expression: {text: 'bang', quotes: true},
334-
raws: {afterValue: '/**/'},
332+
raws: {after: '/**/'},
335333
guarded: true,
336334
},
337335
},
@@ -359,10 +357,10 @@ describe('a configuration map', () => {
359357
it('variables', () => {
360358
expect(clone.size).toBe(2);
361359
const variables = [...clone.variables()];
362-
expect(variables[0]?.variableName).toBe('foo');
360+
expect(variables[0]?.name).toBe('foo');
363361
expect(variables[0]?.parent).toBe(clone);
364362
expect(variables[0]).toHaveStringExpression('expression', 'bar');
365-
expect(variables[1]?.variableName).toBe('baz');
363+
expect(variables[1]?.name).toBe('baz');
366364
expect(variables[1]?.parent).toBe(clone);
367365
expect(variables[1]).toHaveStringExpression('expression', 'bang');
368366
});
@@ -399,7 +397,7 @@ describe('a configuration map', () => {
399397
});
400398
expect(clone.size).toBe(1);
401399
const variables = [...clone.variables()];
402-
expect(variables[0]?.variableName).toBe('zip');
400+
expect(variables[0]?.name).toBe('zip');
403401
expect(variables[0]?.parent).toBe(clone);
404402
expect(variables[0]).toHaveStringExpression('expression', 'zap');
405403
});
@@ -408,10 +406,10 @@ describe('a configuration map', () => {
408406
const clone = original.clone({variables: undefined});
409407
expect(clone.size).toBe(2);
410408
const variables = [...clone.variables()];
411-
expect(variables[0]?.variableName).toBe('foo');
409+
expect(variables[0]?.name).toBe('foo');
412410
expect(variables[0]?.parent).toBe(clone);
413411
expect(variables[0]).toHaveStringExpression('expression', 'bar');
414-
expect(variables[1]?.variableName).toBe('baz');
412+
expect(variables[1]?.name).toBe('baz');
415413
expect(variables[1]?.parent).toBe(clone);
416414
expect(variables[1]).toHaveStringExpression('expression', 'bang');
417415
});

pkg/sass-parser/lib/src/configuration.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export interface ConfigurationProps {
4444
| Array<ConfiguredVariable | ConfiguredVariableProps>;
4545
}
4646

47+
// TODO: This should probably implement a similar interface to `ParameterList`
48+
// as well as or instead of its current map-like interface.
49+
4750
/**
4851
* A configuration map for a `@use` or `@forward` rule.
4952
*
@@ -101,9 +104,9 @@ export class Configuration extends Node {
101104
const realVariable =
102105
'sassType' in variable ? variable : new ConfiguredVariable(variable);
103106
realVariable.parent = this;
104-
const old = this._variables.get(realVariable.variableName);
107+
const old = this._variables.get(realVariable.name);
105108
if (old) old.parent = undefined;
106-
this._variables.set(realVariable.variableName, realVariable);
109+
this._variables.set(realVariable.name, realVariable);
107110
return this;
108111
}
109112

@@ -189,7 +192,7 @@ export class Configuration extends Node {
189192
result += variable.raws.before ?? ' ';
190193
}
191194
result += variable.toString();
192-
result += variable.raws.afterValue ?? '';
195+
result += variable.raws.after ?? '';
193196
}
194197
return result + `${this.raws.comma ? ',' : ''}${this.raws.after ?? ''})`;
195198
}

0 commit comments

Comments
 (0)