diff --git a/executors/dart_web/analysis_options.yaml b/executors/dart_web/analysis_options.yaml index a20fa93f..d2c8848c 100644 --- a/executors/dart_web/analysis_options.yaml +++ b/executors/dart_web/analysis_options.yaml @@ -16,3 +16,5 @@ linter: - avoid_dynamic_calls - type_annotate_public_apis - non_constant_identifier_names + - prefer_final_locals + - unnecessary_this diff --git a/executors/dart_web/bin/collator.dart b/executors/dart_web/bin/collator.dart index f2dafa32..1d057160 100644 --- a/executors/dart_web/bin/collator.dart +++ b/executors/dart_web/bin/collator.dart @@ -8,10 +8,10 @@ import 'package:intl4x/collation.dart'; import 'package:intl4x/intl4x.dart'; String testCollationShort(String jsonEncoded) { - var json = jsonDecode(jsonEncoded) + final json = jsonDecode(jsonEncoded) as Map; // For the moment, use strings for easier interop // Global default locale - var testLocale = 'en'; + final testLocale = 'en'; Map outputLine; // Set up collator object with optional locale and testOptions. @@ -22,12 +22,12 @@ String testCollationShort(String jsonEncoded) { } else { coll = Intl(); } - var d1 = json['string1']; - var d2 = json['string2']; + final d1 = json['string1']; + final d2 = json['string2']; - var collationOptions = CollationOptions(ignorePunctuation: true); - var compared = coll.collation(collationOptions).compare(d1, d2); - var result = compared <= 0 ? true : false; + final collationOptions = CollationOptions(ignorePunctuation: true); + final compared = coll.collation(collationOptions).compare(d1, d2); + final result = compared <= 0 ? true : false; outputLine = {'label': json['label'], 'result': result}; if (result != true) { diff --git a/executors/dart_web/bin/make_runnable_by_node.dart b/executors/dart_web/bin/make_runnable_by_node.dart index cf862a37..a5a6f2c6 100644 --- a/executors/dart_web/bin/make_runnable_by_node.dart +++ b/executors/dart_web/bin/make_runnable_by_node.dart @@ -10,7 +10,7 @@ class ExportFunction { } Future main(List args) async { - var names = { + final names = { 'collator': ExportFunction( name: 'testCollationShort', argNames: ['encoded'], @@ -28,8 +28,8 @@ Future main(List args) async { } Future prepare(String name, ExportFunction function) async { - var outFile = '${name}Dart'; - var compile = await Process.run('dart', [ + final outFile = '${name}Dart'; + final compile = await Process.run('dart', [ 'compile', 'js', 'bin/${name}Executor.dart', @@ -43,11 +43,11 @@ Future prepare(String name, ExportFunction function) async { } void setVersionFile() { - var lockStr = File('pubspec.lock').readAsStringSync(); + final lockStr = File('pubspec.lock').readAsStringSync(); final lockfile = PubspecLock.parse(lockStr); - var version = lockfile.packages['intl4x']?.version; + final version = lockfile.packages['intl4x']?.version; if (version != null) { File('out/version.js').writeAsStringSync(''' const dartVersion = "${version.canonicalizedVersion}"; @@ -58,7 +58,7 @@ module.exports = { dartVersion }; /// Prepare the file to export `testCollationShort` void prepareOutFile(String name, List functions) { - var outFile = File('out/$name.js'); + final outFile = File('out/$name.js'); var s = outFile.readAsStringSync(); s = s.replaceAll('self.', ''); s = s.replaceFirst('(function dartProgram() {', @@ -67,7 +67,7 @@ void prepareOutFile(String name, List functions) { s = s.replaceFirst('(function dartProgram() {', 'module.exports = (function dartProgram() {'); - var exportFunctions = functions + final exportFunctions = functions .map( (e) => '''${e.name}: function(${e.argNames.join(',')}) { return A.${e.name}(${e.argNames.join(',')}); diff --git a/executors/dart_web/bin/numberformat.dart b/executors/dart_web/bin/numberformat.dart index 6affb2ef..4930a614 100644 --- a/executors/dart_web/bin/numberformat.dart +++ b/executors/dart_web/bin/numberformat.dart @@ -96,16 +96,16 @@ String testDecimalFormat( // If options are in the JSON, use them... NumberFormatOptions options; - var jsonOptions = (json['options'] ?? {}) as Map; + final jsonOptions = (json['options'] ?? {}) as Map; if (jsonOptions.isNotEmpty) { - options = fromJson(jsonOptions); + options = _fromJson(jsonOptions); } else { try { - options = decimalPatternToOptions(pattern, rounding); + options = _decimalPatternToOptions(pattern, rounding); } catch (error) { // Some error - to return this message return jsonEncode({ - 'error': "Can't convert pattern", + 'error': 'Can\'t convert pattern', 'label': label, }); } @@ -184,30 +184,30 @@ String testDecimalFormat( }); } - var testLocale = json['locale'] as String?; + final testLocale = json['locale'] as String?; Intl intl; Map outputLine; try { if (testLocale != null) { intl = Intl( - locale: Locale(language: testLocale.split('-').first), + locale: Locale.parse(testLocale), ecmaPolicy: AlwaysEcma(), ); } else { intl = Intl( - locale: Locale(language: 'und'), + locale: const Locale(language: 'und'), ecmaPolicy: AlwaysEcma(), ); } - NumberFormat nf = intl.numberFormat(options); + final NumberFormat nf = intl.numberFormat(options); // TODO: Catch unsupported units, e.g., furlongs. outputLine = { 'label': json['label'], - 'result': jsonEncode(nf.format(input)), - 'actual_options': jsonEncode(jsonOptions) + 'result': nf.format(input), + 'actual_options': options.toMapString(), }; } catch (error) { if (error.toString().contains('furlong')) { @@ -222,23 +222,21 @@ String testDecimalFormat( outputLine = { 'label': json['label'], 'error': 'formatting error: $error', + 'actual_options': options.toMapString(), }; - if (error is RangeError) { - outputLine['error_detail'] = error.message; - outputLine['actual_options'] = jsonEncode(jsonOptions); - } /// Uncomment the line below for easier debugging - rethrow; + // rethrow; } return jsonEncode(outputLine); } -NumberFormatOptions decimalPatternToOptions(String? pattern, String? rounding) { +NumberFormatOptions _decimalPatternToOptions( + String? pattern, String? rounding) { final numberFormatOptions = patternsToOptions[pattern] ?? NumberFormatOptions.custom(); if (rounding != null) { - var roundingMode = + final roundingMode = RoundingMode.values.firstWhere((mode) => mode.name == rounding); return numberFormatOptions.copyWith(roundingMode: roundingMode); } else { @@ -246,16 +244,15 @@ NumberFormatOptions decimalPatternToOptions(String? pattern, String? rounding) { } } -NumberFormatOptions fromJson(Map options) { - print(options); - var unit = Unit.values +NumberFormatOptions _fromJson(Map options) { + final unit = Unit.values .where((element) => element.jsName == options['unit']) .firstOrNull; - var currency = options['currency']; - var currencyDisplay = CurrencyDisplay.values - .where((element) => element.name == 'currencyDisplay') + final currency = options['currency']; + final currencyDisplay = CurrencyDisplay.values + .where((element) => element.name == options['currencyDisplay']) .firstOrNull; - var style = [ + final style = [ DecimalStyle(), if (currency != null) CurrencyStyle( @@ -265,33 +262,67 @@ NumberFormatOptions fromJson(Map options) { if (unit != null) UnitStyle(unit: unit), ].where((element) => element.name == options['style']).firstOrNull; - var compactDisplay = CompactDisplay.values - .where((element) => element.name == 'compactDisplay') + final compactDisplay = CompactDisplay.values + .where((element) => element.name == options['compactDisplay']) .firstOrNull; - var notation = [ + final notation = [ CompactNotation(compactDisplay: compactDisplay ?? CompactDisplay.short), StandardNotation(), ScientificNotation(), EngineeringNotation(), ].where((element) => element.name == options['notation']).firstOrNull; - var unitDisplay = UnitDisplay.values + final unitDisplay = UnitDisplay.values .where((element) => element.name == options['unitDisplay']) .firstOrNull; - var signDisplay = SignDisplay.values + final signDisplay = SignDisplay.values .where((element) => element.name == options['signDisplay']) .firstOrNull; - var localeMatcher = LocaleMatcher.values + final localeMatcher = LocaleMatcher.values .where((element) => element.jsName == options['localeMatcher']) .firstOrNull; - var useGrouping = Grouping.values - .where((element) => element.jsName == options['useGrouping']) + final useGrouping = Grouping.values + .where((element) => element.jsName == options['useGrouping'].toString()) .firstOrNull; - var roundingMode = RoundingMode.values + final roundingMode = RoundingMode.values .where((element) => element.name == options['roundingMode']) .firstOrNull; - var trailingZeroDisplay = TrailingZeroDisplay.values + final trailingZeroDisplay = TrailingZeroDisplay.values .where((element) => element.name == options['trailingZeroDisplay']) .firstOrNull; + final minimumSignificantDigits = options['minimumSignificantDigits'] as int?; + final maximumSignificantDigits = options['maximumSignificantDigits'] as int?; + final roundingIncrement = options['roundingIncrement'] as int?; + final roundingPriority = RoundingPriority.values + .where((element) => element.name == options['roundingPriority']) + .firstOrNull ?? + RoundingPriority.auto; + final minimumFractionDigits = options['minimumFractionDigits'] as int?; + final maximumFractionDigits = options['maximumFractionDigits'] as int?; + + Digits? digits; + if ((minimumFractionDigits != null || maximumFractionDigits != null) && + (minimumSignificantDigits != null || maximumSignificantDigits != null)) { + digits = Digits.withSignificantAndFractionDigits( + maximumFractionDigits: maximumFractionDigits, + maximumSignificantDigits: maximumSignificantDigits, + minimumFractionDigits: minimumFractionDigits, + minimumSignificantDigits: minimumSignificantDigits, + roundingPriority: roundingPriority, + ); + } else if (minimumFractionDigits != null || maximumFractionDigits != null) { + digits = Digits.withFractionDigits( + minimum: minimumFractionDigits, + maximum: maximumFractionDigits, + roundingIncrement: roundingIncrement, + ); + } else if (minimumSignificantDigits != null || + maximumSignificantDigits != null) { + digits = Digits.withSignificantDigits( + minimum: minimumSignificantDigits, + maximum: maximumSignificantDigits, + ); + } + return NumberFormatOptions.custom().copyWith( style: style, currency: currency, @@ -305,7 +336,33 @@ NumberFormatOptions fromJson(Map options) { numberingSystem: options['numberingSystem'], roundingMode: roundingMode, trailingZeroDisplay: trailingZeroDisplay, - minimumIntegerDigits: options['minimumIntegerDigits'] ?? 1, - digits: options['digits'], //TODO: map digit options + minimumIntegerDigits: options['minimumIntegerDigits'], + digits: digits, ); } + +extension on NumberFormatOptions { + Map toMapString() { + return { + 'style': style.name, + 'currency': currency, + 'currencyDisplay': currencyDisplay?.toString(), + 'unit': unit?.jsName, + 'unitDisplay': unitDisplay?.toString(), + 'localeMatcher': localeMatcher.jsName, + 'signDisplay': signDisplay.name, + 'notation': notation.name, + 'useGrouping': useGrouping.jsName, + 'numberingSystem': numberingSystem?.toString(), + 'roundingMode': roundingMode.name, + 'trailingZeroDisplay': trailingZeroDisplay.name, + 'minimumIntegerDigits': minimumIntegerDigits, + 'digits': { + 'fractionDigits': digits?.fractionDigits.toString(), + 'significantDigits': digits?.significantDigits.toString(), + 'roundingPriority': digits?.roundingPriority?.toString(), + 'roundingIncrement': digits?.roundingIncrement?.toString(), + }, + }; + } +} diff --git a/executors/dart_web/test/number_fmt.dart b/executors/dart_web/test/number_fmt.dart new file mode 100644 index 00000000..a72818de --- /dev/null +++ b/executors/dart_web/test/number_fmt.dart @@ -0,0 +1,51 @@ +@TestOn('browser') + +import 'dart:async'; +import 'dart:convert'; + +import 'package:test/test.dart'; + +import '../bin/numberformat.dart'; + +void main() { + testWithFormatting('Check number format output', () { + final inputLine = { + 'label': '0001', + 'locale': 'es-MX', + 'skeleton': 'compact-short percent unit-width-narrow', + 'input': '91827.3645', + 'options': { + 'notation': 'compact', + 'compactDisplay': 'short', + 'style': 'unit', + 'unit': 'percent', + 'unitDisplay': 'narrow', + 'currencyDisplay': 'narrowSymbol' + } + }; + final outputLine = testDecimalFormat(jsonEncode(inputLine)); + print(outputLine); + }); +} + +void testWithFormatting( + dynamic description, + T Function() body, { + String? testOn, + Timeout? timeout, + dynamic skip, + dynamic tags, + Map? onPlatform, + int? retry, +}) { + test( + description, + () => runZoned(body, zoneValues: {#test.allowFormatting: true}), + testOn: testOn, + timeout: timeout, + skip: skip, + tags: tags, + onPlatform: onPlatform, + retry: retry, + ); +}