-
Notifications
You must be signed in to change notification settings - Fork 369
Expose calculations in JS API #1988
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 9 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
f5ce090
Expose calculations in JS API
jerivas fbfe7f1
Merge branch 'main' into js-api-calculations
jgerigmeyer 8c9a7d8
Use strings for CalculationOperator in JS
jerivas 64d50c8
Merge branch 'main' into js-api-calculations
jgerigmeyer fb9bb04
Update date
jerivas 3f7b3ab
Fix call stack errors in JS
jerivas bdb6773
Refactor static methods
jerivas f09910f
Return an immutable list of arguments
jerivas 13b4f33
No need to define left/right
jerivas 71aa9a2
Fix call stack errors on operator access
jerivas 6277580
Export calculations classes to the browser
jerivas 60243ca
Simplify custom function return values
jerivas c63fa7e
lint
jerivas 70e567e
Parse `value` and `max` from `min`
jerivas b4fff6c
Merge branch 'main' into js-api-calculations
jerivas b4e9ade
Address changes to the clamp spec
jerivas 024c239
Improve simplification implementation
jerivas 399dd25
Update after spec changes
jerivas b292fb8
Streamline simplification
jerivas e300e41
Check for Value before and after simplification
jerivas e70e5e3
Address review
jerivas d30acbd
Uniform error messages
jerivas cd06089
Address review
jerivas f3d169e
Clean up
jerivas 6b3b5b8
Test assertCalculation in other types
jerivas 52ab266
Add basic calculation tests
jerivas 2928f3e
Update pubspec and changelog
nex3 859069e
Merge branch 'main' into js-api-calculations
nex3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// Copyright 2023 Google Inc. Use of this source code is governed by an | ||
// MIT-style license that can be found in the LICENSE file or at | ||
// https://opensource.org/licenses/MIT. | ||
|
||
import 'package:collection/collection.dart'; | ||
import 'package:node_interop/js.dart'; | ||
import 'package:sass/src/node/immutable.dart'; | ||
import 'package:sass/src/node/utils.dart'; | ||
|
||
import '../../value.dart'; | ||
import '../reflection.dart'; | ||
|
||
/// Check that [arg] is a valid argument to a calculation function. | ||
void isCalculationValue(Object arg, {bool checkUnquoted = true}) { | ||
if (arg is! SassNumber && | ||
arg is! SassString && | ||
arg is! SassCalculation && | ||
arg is! CalculationOperation && | ||
arg is! CalculationInterpolation) { | ||
jsThrow(JsError('Argument must be one of ' | ||
'SassNumber, SassString, SassCalculation, CalculationOperation, ' | ||
'CalculationInterpolation')); | ||
} | ||
if (checkUnquoted && arg is SassString && arg.hasQuotes) { | ||
jsThrow(JsError('Argument must be unquoted SassString')); | ||
} | ||
} | ||
|
||
/// The JavaScript `SassCalculation` class. | ||
final JSClass calculationClass = () { | ||
var jsClass = | ||
createJSClass('sass.SassCalculation', (Object self, [Object? _]) { | ||
jsThrow(JsError("new sass.SassCalculation() isn't allowed")); | ||
}); | ||
|
||
jsClass.defineStaticMethods({ | ||
'calc': (Object argument) { | ||
isCalculationValue(argument); | ||
return SassCalculation.unsimplified('calc', [argument]); | ||
}, | ||
'min': (Object arguments) { | ||
var argList = jsToDartList(arguments).cast<Object>(); | ||
argList.forEach(isCalculationValue); | ||
return SassCalculation.unsimplified('min', argList); | ||
}, | ||
'max': (Object arguments) { | ||
var argList = jsToDartList(arguments).cast<Object>(); | ||
argList.forEach(isCalculationValue); | ||
return SassCalculation.unsimplified('max', argList); | ||
}, | ||
'clamp': (Object min, [Object? value, Object? max]) { | ||
if (value == null && max != null) { | ||
jsThrow(JsError('`value` is undefined and `max` is not undefined')); | ||
} | ||
if ((value == null || max == null) && | ||
!(min is SassString && min.text.startsWith('var(')) && | ||
!(value is SassString && value.text.startsWith('var('))) { | ||
jsThrow(JsError( | ||
'`value` or `max` is undefined and neither `min` nor `value` is a SassString that begins with "var("')); | ||
} | ||
[min, value, max].whereNotNull().forEach(isCalculationValue); | ||
return SassCalculation.unsimplified( | ||
'clamp', [min, value, max].whereNotNull()); | ||
}, | ||
}); | ||
|
||
jsClass.defineMethods({ | ||
'assertCalculation': (SassCalculation self, [String? name]) => self, | ||
}); | ||
|
||
jsClass.defineGetters({ | ||
// The `name` getter is included by default by `createJSClass` | ||
jerivas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
'arguments': (SassCalculation self) => ImmutableList(self.arguments), | ||
}); | ||
|
||
getJSClass(SassCalculation.unsimplified('calc', [SassNumber(1)])) | ||
.injectSuperclass(jsClass); | ||
jerivas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return jsClass; | ||
}(); | ||
|
||
/// The JavaScript CalculationOperation class | ||
final JSClass calculationOperationClass = () { | ||
var jsClass = createJSClass('sass.CalculationOperation', | ||
(Object self, String strOperator, Object left, Object right) { | ||
var operator = CalculationOperator.values | ||
.firstWhereOrNull((value) => value.operator == strOperator); | ||
if (operator == null) { | ||
jsThrow(JsError('Invalid operator: $strOperator')); | ||
} | ||
isCalculationValue(left, checkUnquoted: false); | ||
isCalculationValue(right, checkUnquoted: false); | ||
return SassCalculation.operateInternal(operator, left, right, | ||
inMinMax: false, simplify: false); | ||
}); | ||
|
||
jsClass.defineMethods({ | ||
'equals': (CalculationOperation self, Object other) => self == other, | ||
'hashCode': (CalculationOperation self) => self.hashCode, | ||
}); | ||
|
||
jsClass.defineGetters({ | ||
'operator': (CalculationOperation self) => self.operator.operator, | ||
jerivas marked this conversation as resolved.
Show resolved
Hide resolved
jerivas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
|
||
getJSClass(SassCalculation.operateInternal( | ||
CalculationOperator.plus, SassNumber(1), SassNumber(1), | ||
inMinMax: false, simplify: false)) | ||
.injectSuperclass(jsClass); | ||
return jsClass; | ||
}(); | ||
|
||
/// The JavaScript CalculationInterpolation class | ||
final JSClass calculationInterpolationClass = () { | ||
jerivas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var jsClass = createJSClass('sass.CalculationInterpolation', | ||
(Object self, String value) => CalculationInterpolation(value)); | ||
|
||
jsClass.defineMethods({ | ||
'equals': (CalculationInterpolation self, Object other) => self == other, | ||
'hashCode': (CalculationInterpolation self) => self.hashCode, | ||
}); | ||
|
||
jsClass.defineGetters({ | ||
'value': (CalculationInterpolation self) => self.value, | ||
}); | ||
|
||
getJSClass(CalculationInterpolation('')).injectSuperclass(jsClass); | ||
return jsClass; | ||
}(); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.