Skip to content

Commit 786ab6c

Browse files
authored
Add support for SassCalculation (#237)
1 parent acfb215 commit 786ab6c

File tree

5 files changed

+366
-10
lines changed

5 files changed

+366
-10
lines changed

lib/index.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ export const compileAsync = sass.compileAsync;
55
export const compileString = sass.compileString;
66
export const compileStringAsync = sass.compileStringAsync;
77
export const Logger = sass.Logger;
8+
export const CalculationInterpolation = sass.CalculationInterpolation
9+
export const CalculationOperation = sass.CalculationOperation
10+
export const CalculationOperator = sass.CalculationOperator
811
export const SassArgumentList = sass.SassArgumentList;
912
export const SassBoolean = sass.SassBoolean;
13+
export const SassCalculation = sass.SassCalculation
1014
export const SassColor = sass.SassColor;
1115
export const SassFunction = sass.SassFunction;
1216
export const SassList = sass.SassList;
@@ -59,6 +63,18 @@ export default {
5963
defaultExportDeprecation();
6064
return sass.Logger;
6165
},
66+
get CalculationOperation() {
67+
defaultExportDeprecation();
68+
return sass.CalculationOperation;
69+
},
70+
get CalculationOperator() {
71+
defaultExportDeprecation();
72+
return sass.CalculationOperator;
73+
},
74+
get CalculationInterpolation() {
75+
defaultExportDeprecation();
76+
return sass.CalculationInterpolation;
77+
},
6278
get SassArgumentList() {
6379
defaultExportDeprecation();
6480
return sass.SassArgumentList;
@@ -67,6 +83,10 @@ export default {
6783
defaultExportDeprecation();
6884
return sass.SassBoolean;
6985
},
86+
get SassCalculation() {
87+
defaultExportDeprecation();
88+
return sass.SassCalculation;
89+
},
7090
get SassColor() {
7191
defaultExportDeprecation();
7292
return sass.SassColor;

lib/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export {SassNumber} from './src/value/number';
1616
export {SassString} from './src/value/string';
1717
export {Value} from './src/value';
1818
export {sassNull} from './src/value/null';
19+
export {
20+
CalculationOperation,
21+
CalculationOperator,
22+
CalculationInterpolation,
23+
SassCalculation,
24+
} from './src/value/calculations';
1925

2026
export * as types from './src/legacy/value';
2127
export {Exception} from './src/exception';

lib/src/protofier.ts

Lines changed: 192 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ import {SassString} from './value/string';
1717
import {Value} from './value';
1818
import {sassNull} from './value/null';
1919
import {sassTrue, sassFalse} from './value/boolean';
20+
import {
21+
CalculationValue,
22+
SassCalculation,
23+
CalculationInterpolation,
24+
CalculationOperation,
25+
CalculationOperator,
26+
} from './value/calculations';
2027

2128
/**
2229
* A class that converts [Value] objects into protobufs.
@@ -56,11 +63,7 @@ export class Protofier {
5663
string.quoted = value.hasQuotes;
5764
result.value = {case: 'string', value: string};
5865
} else if (value instanceof SassNumber) {
59-
const number = new proto.Value_Number();
60-
number.value = value.value;
61-
number.numerators = value.numeratorUnits.toArray();
62-
number.denominators = value.denominatorUnits.toArray();
63-
result.value = {case: 'number', value: number};
66+
result.value = {case: 'number', value: this.protofyNumber(value)};
6467
} else if (value instanceof SassColor) {
6568
if (value.hasCalculatedHsl) {
6669
const color = new proto.Value_HslColor();
@@ -116,6 +119,11 @@ export class Protofier {
116119
fn.signature = value.signature!;
117120
result.value = {case: 'hostFunction', value: fn};
118121
}
122+
} else if (value instanceof SassCalculation) {
123+
result.value = {
124+
case: 'calculation',
125+
value: this.protofyCalculation(value),
126+
};
119127
} else if (value === sassTrue) {
120128
result.value = {case: 'singleton', value: proto.SingletonValue.TRUE};
121129
} else if (value === sassFalse) {
@@ -128,6 +136,15 @@ export class Protofier {
128136
return result;
129137
}
130138

139+
/** Converts `number` to its protocol buffer representation. */
140+
private protofyNumber(number: SassNumber): proto.Value_Number {
141+
return new proto.Value_Number({
142+
value: number.value,
143+
numerators: number.numeratorUnits.toArray(),
144+
denominators: number.denominatorUnits.toArray(),
145+
});
146+
}
147+
131148
/** Converts `separator` to its protocol buffer representation. */
132149
private protofySeparator(separator: ListSeparator): proto.ListSeparator {
133150
switch (separator) {
@@ -144,6 +161,68 @@ export class Protofier {
144161
}
145162
}
146163

164+
/** Converts `calculation` to its protocol buffer representation. */
165+
private protofyCalculation(
166+
calculation: SassCalculation
167+
): proto.Value_Calculation {
168+
return new proto.Value_Calculation({
169+
name: calculation.name,
170+
arguments: calculation.arguments
171+
.map(this.protofyCalculationValue.bind(this))
172+
.toArray(),
173+
});
174+
}
175+
176+
/** Converts a CalculationValue that appears within a `SassCalculation` to
177+
* its protocol buffer representation. */
178+
private protofyCalculationValue(
179+
value: Object
180+
): proto.Value_Calculation_CalculationValue {
181+
const result = new proto.Value_Calculation_CalculationValue();
182+
if (value instanceof SassCalculation) {
183+
result.value = {
184+
case: 'calculation',
185+
value: this.protofyCalculation(value),
186+
};
187+
} else if (value instanceof CalculationOperation) {
188+
result.value = {
189+
case: 'operation',
190+
value: new proto.Value_Calculation_CalculationOperation({
191+
operator: this.protofyCalculationOperator(value.operator),
192+
left: this.protofyCalculationValue(value.left),
193+
right: this.protofyCalculationValue(value.right),
194+
}),
195+
};
196+
} else if (value instanceof CalculationInterpolation) {
197+
result.value = {case: 'interpolation', value: value.value};
198+
} else if (value instanceof SassString) {
199+
result.value = {case: 'string', value: value.text};
200+
} else if (value instanceof SassNumber) {
201+
result.value = {case: 'number', value: this.protofyNumber(value)};
202+
} else {
203+
throw utils.compilerError(`Unknown CalculationValue ${value}`);
204+
}
205+
return result;
206+
}
207+
208+
/** Converts `operator` to its protocol buffer representation. */
209+
private protofyCalculationOperator(
210+
operator: CalculationOperator
211+
): proto.CalculationOperator {
212+
switch (operator) {
213+
case '+':
214+
return proto.CalculationOperator.PLUS;
215+
case '-':
216+
return proto.CalculationOperator.MINUS;
217+
case '*':
218+
return proto.CalculationOperator.TIMES;
219+
case '/':
220+
return proto.CalculationOperator.DIVIDE;
221+
default:
222+
throw utils.compilerError(`Unknown CalculationOperator ${operator}`);
223+
}
224+
}
225+
147226
/** Converts `value` to its JS representation. */
148227
deprotofy(value: proto.Value): Value {
149228
switch (value.value.case) {
@@ -155,11 +234,7 @@ export class Protofier {
155234
}
156235

157236
case 'number': {
158-
const number = value.value.value;
159-
return new SassNumber(number.value, {
160-
numeratorUnits: number.numerators,
161-
denominatorUnits: number.denominators,
162-
});
237+
return this.deprotofyNumber(value.value.value);
163238
}
164239

165240
case 'rgbColor': {
@@ -247,6 +322,9 @@ export class Protofier {
247322
'The compiler may not send Value.host_function.'
248323
);
249324

325+
case 'calculation':
326+
return this.deprotofyCalculation(value.value.value);
327+
250328
case 'singleton':
251329
switch (value.value.value) {
252330
case proto.SingletonValue.TRUE:
@@ -263,6 +341,14 @@ export class Protofier {
263341
}
264342
}
265343

344+
/** Converts `number` to its JS representation. */
345+
private deprotofyNumber(number: proto.Value_Number): SassNumber {
346+
return new SassNumber(number.value, {
347+
numeratorUnits: number.numerators,
348+
denominatorUnits: number.denominators,
349+
});
350+
}
351+
266352
/** Converts `separator` to its JS representation. */
267353
private deprotofySeparator(separator: proto.ListSeparator): ListSeparator {
268354
switch (separator) {
@@ -278,4 +364,100 @@ export class Protofier {
278364
throw utils.compilerError(`Unknown separator ${separator}`);
279365
}
280366
}
367+
368+
/** Converts `calculation` to its Sass representation. */
369+
private deprotofyCalculation(
370+
calculation: proto.Value_Calculation
371+
): SassCalculation {
372+
switch (calculation.name) {
373+
case 'calc':
374+
if (calculation.arguments.length !== 1) {
375+
throw utils.compilerError(
376+
'Value.Calculation.arguments must have exactly one argument for calc().'
377+
);
378+
}
379+
return SassCalculation.calc(
380+
this.deprotofyCalculationValue(calculation.arguments[0])
381+
);
382+
case 'clamp':
383+
if (calculation.arguments.length !== 3) {
384+
throw utils.compilerError(
385+
'Value.Calculation.arguments must have exactly 3 arguments for clamp().'
386+
);
387+
}
388+
return SassCalculation.clamp(
389+
this.deprotofyCalculationValue(calculation.arguments[0]),
390+
this.deprotofyCalculationValue(calculation.arguments[1]),
391+
this.deprotofyCalculationValue(calculation.arguments[2])
392+
);
393+
case 'min':
394+
if (calculation.arguments.length === 0) {
395+
throw utils.compilerError(
396+
'Value.Calculation.arguments must have at least 1 argument for min().'
397+
);
398+
}
399+
return SassCalculation.min(
400+
calculation.arguments.map(this.deprotofyCalculationValue)
401+
);
402+
case 'max':
403+
if (calculation.arguments.length === 0) {
404+
throw utils.compilerError(
405+
'Value.Calculation.arguments must have at least 1 argument for max().'
406+
);
407+
}
408+
return SassCalculation.max(
409+
calculation.arguments.map(this.deprotofyCalculationValue)
410+
);
411+
default:
412+
throw utils.compilerError(
413+
`Value.Calculation.name "${calculation.name}" is not a recognized calculation type.`
414+
);
415+
}
416+
}
417+
418+
/** Converts `value` to its Sass representation. */
419+
private deprotofyCalculationValue(
420+
value: proto.Value_Calculation_CalculationValue
421+
): CalculationValue {
422+
switch (value.value.case) {
423+
case 'number':
424+
return this.deprotofyNumber(value.value.value);
425+
case 'calculation':
426+
return this.deprotofyCalculation(value.value.value);
427+
case 'string':
428+
return new SassString(value.value.value, {quotes: false});
429+
case 'operation':
430+
return new CalculationOperation(
431+
this.deprotofyCalculationOperator(value.value.value.operator),
432+
this.deprotofyCalculationValue(
433+
value.value.value.left as proto.Value_Calculation_CalculationValue
434+
),
435+
this.deprotofyCalculationValue(
436+
value.value.value.right as proto.Value_Calculation_CalculationValue
437+
)
438+
);
439+
case 'interpolation':
440+
return new CalculationInterpolation(value.value.value);
441+
default:
442+
throw utils.mandatoryError('Calculation.CalculationValue.value');
443+
}
444+
}
445+
446+
/** Converts `operator` to its Sass representation. */
447+
private deprotofyCalculationOperator(
448+
operator: proto.CalculationOperator
449+
): CalculationOperator {
450+
switch (operator) {
451+
case proto.CalculationOperator.PLUS:
452+
return '+';
453+
case proto.CalculationOperator.MINUS:
454+
return '-';
455+
case proto.CalculationOperator.TIMES:
456+
return '*';
457+
case proto.CalculationOperator.DIVIDE:
458+
return '/';
459+
default:
460+
throw utils.compilerError(`Unknown CalculationOperator ${operator}`);
461+
}
462+
}
281463
}

0 commit comments

Comments
 (0)