diff --git a/CHANGELOG.md b/CHANGELOG.md index 52f6961..c34f737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 1.6.1 +- Bump dependencies + ## 1.6.0 ### Feat - Added `tests` diff --git a/analysis_options.yaml b/analysis_options.yaml index 536e1b7..896ff3c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1,9 @@ -include: package:flutter_lint_rules/analysis_options_easy.yaml \ No newline at end of file +################################################################################## +## Linter rules for all packages ## +## ----------------------------- ## +## Note that there is no need to define another ## +## analysis-options.yaml in each package because they will ## +## automatically go up the tree if not found in the package. See here: ## +## https://dart.dev/guides/language/analysis-options#the-analysis-options-file ## +################################################################################## +include: package:flutter_lints/flutter.yaml \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 4d0892e..9ae6f9c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -116,10 +116,10 @@ packages: dependency: transitive description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.1" http_parser: dependency: transitive description: @@ -128,6 +128,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -140,34 +164,34 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" package_info_plus: dependency: transitive description: name: package_info_plus - sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097 url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "6.0.0" package_info_plus_platform_interface: dependency: transitive description: @@ -180,10 +204,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" plugin_platform_interface: dependency: transitive description: @@ -261,14 +285,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" web: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.1" win32: dependency: transitive description: @@ -278,5 +310,5 @@ packages: source: hosted version: "4.1.4" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.6.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.lock b/pubspec.lock index bb2f1da..1957305 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -77,10 +77,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -138,7 +138,7 @@ packages: source: hosted version: "4.8.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -161,14 +161,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" - dart_code_metrics_presets: - dependency: transitive - description: - name: dart_code_metrics_presets - sha256: "06e1aea3e4c2beb555ed1ac92366fbc8040ad0b36a0dbd4ec501c11cdb3e57a8" - url: "https://pub.dev" - source: hosted - version: "2.7.0" dart_style: dependency: transitive description: @@ -214,23 +206,14 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_lint_rules: - dependency: "direct dev" - description: - path: "." - ref: main - resolved-ref: "7ef050b7e13b57f088b93ec7e9acfc643bd159ae" - url: "https://github.com/ziqq/flutter_lint_rules.git" - source: git - version: "2.1.2" flutter_lints: - dependency: transitive + dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.2" flutter_test: dependency: "direct dev" description: flutter @@ -269,10 +252,10 @@ packages: dependency: "direct main" description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -313,14 +296,38 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" logging: dependency: transitive description: @@ -333,26 +340,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: - dependency: transitive + dependency: "direct main" description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -365,10 +372,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "4b693867cee1853c9d1d7ecc1871f27f39b2ef2c13c0d8d8507dfe5bebd8aaf1" + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" url: "https://pub.dev" source: hosted - version: "5.4.3" + version: "5.4.4" package_config: dependency: transitive description: @@ -381,10 +388,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097 url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "6.0.0" package_info_plus_platform_interface: dependency: transitive description: @@ -397,10 +404,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" plugin_platform_interface: dependency: transitive description: @@ -542,6 +549,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" watcher: dependency: transitive description: @@ -554,10 +569,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.1" web_socket_channel: dependency: transitive description: @@ -583,5 +598,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.6.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index f2049ef..9270349 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ name: flutter_in_store_app_version_checker description: > A lightweight flutter plugin to check if your app is up-to-date on GooglePlay or AppStore. -version: 1.6.0 +version: 1.6.1 homepage: https://github.com/ziqq/flutter_in_store_app_version_checker @@ -19,20 +19,22 @@ dependencies: flutter: sdk: flutter - http: ^1.1.2 - package_info_plus: ^5.0.1 + # Utility + meta: any + collection: any + + http: ^1.2.1 + package_info_plus: ^6.0.0 dev_dependencies: flutter_test: sdk: flutter - build_runner: ^2.4.7 - mockito: ^5.4.3 + build_runner: ^2.4.9 + mockito: ^5.4.4 - flutter_lint_rules: - git: - url: https://github.com/ziqq/flutter_lint_rules.git - ref: main + # Linting + flutter_lints: ^3.0.2 flutter: plugin: diff --git a/tool/dart/rename_project.dart b/tool/dart/rename_project.dart new file mode 100644 index 0000000..1ae84c8 --- /dev/null +++ b/tool/dart/rename_project.dart @@ -0,0 +1,99 @@ +import 'dart:io' as io; + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as p; + +const String _defaultName = 'flutter_template_name'; +const String _defaultOrganization = 'dev.flutter.template'; +const String _defaultDescription = 'flutter_template_description'; + +/// dart run tool/dart/rename_project.dart --name="qqq" --organization="www" --description="eee" +void main([List? args]) { + if (args == null || args.isEmpty) _throwArguments(); + String? extractArg(String key) { + final value = args.firstWhereOrNull((e) => e.startsWith(key)); + if (value == null) return null; + return RegExp(r'[\d\w\.\-\_ ]+') + .allMatches(value.substring(key.length)) + .map((e) => e.group(0)) + .join() + .trim(); + } + + final name = extractArg('--name'); + final org = extractArg('--organization'); + final desc = extractArg('--description'); + if (name == null || org == null || desc == null) _throwArguments(); + _renameDirectory(_defaultName, name); + _changeContent([ + (from: _defaultName, to: name), + (from: _defaultOrganization, to: org), + (from: _defaultDescription, to: desc), + ]); +} + +Never _throwArguments() { + io.stderr.writeln('Pass arguments: ' + '--name="name" ' + '--organization="org.domain" ' + '--description="description"'); + io.exit(1); +} + +Iterable _recursiveDirectories( + io.Directory directory) sync* { + const excludeFiles = { + 'README.md', + 'rename_project.dart', + }; + const includeExtensions = { + '.dart', + '.yaml', + '.gradle', + '.xml', + '.kt', + '.plist', + '.txt', + '.cc', + '.cpp', + '.rc', + '.xcconfig', + '.pbxproj', + '.xcscheme', + '.html', + '.json', + }; + for (final e in directory.listSync(followLinks: false)) { + if (p.basename(e.path).startsWith('.')) continue; + if (e is io.File) { + if (!includeExtensions.contains(p.extension(e.path))) continue; + if (excludeFiles.contains(p.basename(e.path))) continue; + yield e; + } else if (e is io.Directory) { + yield e; + yield* _recursiveDirectories(e); + } + } +} + +void _renameDirectory(String from, String to) => + _recursiveDirectories(io.Directory.current) + .whereType() + .toList(growable: false) + .where((dir) => p.basename(dir.path) == from) + .forEach((dir) => dir.renameSync(p.join(p.dirname(dir.path), to))); + +void _changeContent(List<({String from, String to})> pairs) => + _recursiveDirectories(io.Directory.current) + .whereType() + .forEach((e) { + var content = e.readAsStringSync(); + var changed = false; + for (final pair in pairs) { + if (!content.contains(pair.from)) continue; + content = content.replaceAll(pair.from, pair.to); + changed = true; + } + if (!changed) return; + e.writeAsStringSync(content); + }); diff --git a/tool/makefile/main.mk b/tool/makefile/main.mk new file mode 100644 index 0000000..cf913f7 --- /dev/null +++ b/tool/makefile/main.mk @@ -0,0 +1,85 @@ +.PHONY: doctor version get clean clean-all clean-cache clean-and-generate fluttergen l10n build_runner codegen gen upgrade upgrade outdated dependencies locales run-build-runner run-build-runner-watch + +# Check flutter doctor +doctor: + @flutter doctor + +# Check flutter version +version: + @flutter --version + +# Get dependencies +get: + @echo "╠ GET DEPENDENCIES..." + @flutter pub get || (echo "▓▓ Get dependencies error ▓▓"; exit 1) + @echo "╠ DEPENDENCIES GETED SUCCESSFULLY" + +# Clean the pub cache +clean-cache: + @echo "╠ CLEAN PUB CACHE" + @flutter pub cache repair + @echo "╠ PUB CACHE CLEANED SUCCESSFULLY" + +# Clean all generated files and run build_runner:build +clean-and-generate: clean build-runner + +# Generate assets +fluttergen: + @echo "╠ RUN FLUTTERGEN..." + @dart pub global activate flutter_gen + @fluttergen -c pubspec.yaml || (echo "▓▓ Fluttergen error ▓▓"; exit 1) + @echo "╠ FLUTTERGEN SUCCESSFULLY" + +# Generate localization +l10n: + @dart pub global activate intl_utils + @(dart pub global run intl_utils:generate) + @(flutter gen-l10n --arb-dir lib/src/core/localization --output-dir lib/src/core/localization/generated --template-arb-file intl_ru.arb) + +# Generate code +codegen: get fluttergen l10n build_runner format + +# Fix code +fix: format + @dart fix --apply lib + +# Generate all +gen: codegen + +# Upgrade dependencies +upgrade: + @echo "╠ RUN UPGRADE DEPENDENCIES..." + @flutter pub upgrade || (echo "▓▓ Upgrade error ▓▓"; exit 1) + @echo "╠ DEPENDENCIES UPGRADED SUCCESSFULLY" + +# Upgrade to major versions +upgrade-major: + @echo "╠ RUN UPGRADE DEPENDENCIES TO MAJOR VERSIONS..." + @flutter pub upgrade --major-versions + @echo "╠ DEPENDENCIES UPGRADED SUCCESSFULLY" + +# Check outdated dependencies +outdated: get + @flutter pub outdated + +# Check outdated dependencies +dependencies: upgrade + @flutter pub outdated --dependency-overrides \ + --dev-dependencies --prereleases --show-all --transitive + +# Runs generate locales +locales: + @echo "╠ CREATE LOCALES" + @flutter gen-l10n + @echo "╠ LOCALES CREATED SUCCESSFULLY" + +# Run build_runner:build +build-runner: + @echo "╠ RUN BUILD RUNNER:BUILD" + @dart --disable-analytics && dart run build_runner build --delete-conflicting-outputs --release + @echo "╠ BUILD RUNNER:BUILD SUCCESSFULLY" + +# Run build_runner:watch +build-runner-watch: + @echo "╠ RUN BUILD RUNNER:WATCH" + @dart --disable-analytics && dart run build_runner watch --delete-conflicting-outputs \ No newline at end of file diff --git a/tool/makefile/pub.mk b/tool/makefile/pub.mk new file mode 100644 index 0000000..ce549df --- /dev/null +++ b/tool/makefile/pub.mk @@ -0,0 +1,34 @@ +.PHONY: get format analyze check publish + +# Get dependencies +get: + @echo "╠ RUN GET DEPENDENCIES..." + @flutter pub get || (echo "▓▓ Get dependencies error ▓▓"; exit 1) + @echo "╠ DEPENDENCIES GETED SUCCESSFULLY" + +# Format code +format: + @echo "╠ RUN FORMAT THE CODE..." + @dart format --fix -l 80 . || (echo "▓▓ Format code error ▓▓"; exit 1) + @(dart format --fix -l 80 . || (echo "▓▓ Format code error ▓▓"; exit 2)) + @echo "╠ CODE FORMATED SUCCESSFULLY" + +# Analyze code +analyze: get format + @echo "╠ RUN ANALYZE THE CODE..." + @dart analyze --fatal-infos --fatal-warnings + @echo "╠ ANALYZED CODE SUCCESSFULLY" + +# Check code +check: analyze + @echo "╠ RUN CECK CODE..." + @dart pub publish --dry-run + @dart pub global activate pana + @pana --json --no-warning --line-length 80 > log.pana.json + @echo "╠ CECKED CODE SUCCESSFULLY" + +# Publish package +publish: + @echo "╠ RUN PUBLISHING..." + @dart pub publish || (echo "▓▓ Publish error ▓▓"; exit 1) + @echo "╠ PUBLISH PACKAGE SUCCESSFULLY" \ No newline at end of file diff --git a/tool/makefile/setup.mk b/tool/makefile/setup.mk new file mode 100644 index 0000000..ac02500 --- /dev/null +++ b/tool/makefile/setup.mk @@ -0,0 +1,38 @@ +.PHONY: icons splash-screen init-firebase + +# Run generate launcher icons used https://pub.dev/packages/flutter_launcher_icons +icons: + @echo "╠ CREATE ICONS" + @dart run flutter_launcher_icons:main -f flutter_launcher_icons* || (echo "▓▓ Create icons error ▓▓"; exit 1) + @echo "╠ ICONS CREATED SUCCESSFULLY" + +# Run generate app splash screen used https://pub.dev/packages/flutter_native_splash +splash-screen: + @echo "╠ CREATE SPLASH SCREEN" + @dart run flutter_native_splash:create || (echo "▓▓ Create splash screen error ▓▓"; exit 1) + @echo "╠ SPLASH SCREEN CREATED SUCCESSFULLY" + +init-firebase: + @npm install -g firebase-tools + @firebase login + @firebase init +# @dart pub global activate flutterfire_cli +# @flutterfire configure \ +# -i tld.domain.app \ +# -m tld.domain.app \ +# -a tld.domain.app \ +# -p project \ +# -e email@gmail.com \ +# -o lib/src/common/constant/firebase_options.g.dart + +# "assets/images/products\/(\d+)\/(\d+)\.jpg" +#downscale-images: +# @cd assets/data/images +# @find . -name "*.jpg" -exec mogrify -format webp {} \; +# @find . -name "*.jpeg" -exec mogrify -format webp {} \; +# @find . -name "*.png" -exec mogrify -format webp {} \; +# @find . -name "*.jpg" -exec rm -f {} \; +# @find . -name "*.jpeg" -exec rm -f {} \; +# @find . -name "*.png" -exec rm -f {} \; +# @find . -type f \( -name "0.webp" -o -name "1.webp" -o -name "2.webp" -o -name "3.webp" -o -name "4.webp" -o -name "5.webp" -o -name "6.webp" -o -name "7.webp" -o -name "8.webp" -o -name "9.webp" \) -exec mogrify -resize '512x512>' -quality 80 {} \; +# @find . -name "thumbnail.webp" -exec mogrify -resize '256x256>' -quality 75 {} \; \ No newline at end of file diff --git a/tool/makefile/test.mk b/tool/makefile/test.mk new file mode 100644 index 0000000..f9a3790 --- /dev/null +++ b/tool/makefile/test.mk @@ -0,0 +1,19 @@ +.PHONY: get-packages-clean format + +# Runs getting the packages in root directory and in each package directory and clean cache +get-packages-clean: get_packages clean_cache + +# Format code +format: + @echo "╠ RUN FORMAT THE CODE..." + @dart format --fix -l 80 . || (echo "Format code error"; exit 1) + @(dart format --fix -l 80 . || (echo "Format code error"; exit 2)) + @echo "╠ CODE FORMATED SUCCESSFULLY" + +# Runs get coverage +coverage: + @lcov --summary coverage/lcov.info +# Runs generage coverage html +genhtml: + @genhtml coverage/lcov.info -o coverage/html +