diff --git a/mobile-v2/analysis_options.yaml b/mobile-v2/analysis_options.yaml index 2ac219e3d3..2a922cbbf1 100644 --- a/mobile-v2/analysis_options.yaml +++ b/mobile-v2/analysis_options.yaml @@ -8,6 +8,8 @@ linter: - unawaited_futures analyzer: + plugins: + - custom_lint exclude: - openapi/** @@ -61,3 +63,26 @@ dart_code_metrics: - prefer-single-widget-per-file: ignore-private-widgets: true - prefer-correct-callback-field-name: false + +custom_lint: + rules: + - import_rule_photo_manager: + message: photo_manager must only be used in MediaRepositories + restrict: package:photo_manager + allowed: + - 'lib/domain/repositories/device_{album,asset}.repository.dart' + - import_rule_drift: + message: drift must only be used in entities and repositories + restrict: package:drift + allowed: + - lib/domain/entities/* + - lib/domain/repositories/*.repository.dart + - lib/utils/extensions/drift.extension.dart + + - import_rule_openapi: + message: openapi must only be used through Api Repositories + restrict: package:openapi + allowed: + - lib/domain/repositories/api/* + - lib/utils/immich_api_client.dart + - lib/utils/openapi_patching.dart diff --git a/mobile-v2/immich_lint/analysis_options.yaml b/mobile-v2/immich_lint/analysis_options.yaml new file mode 100644 index 0000000000..572dd239d0 --- /dev/null +++ b/mobile-v2/immich_lint/analysis_options.yaml @@ -0,0 +1 @@ +include: package:lints/recommended.yaml diff --git a/mobile-v2/immich_lint/lib/immich_mobile_lint.dart b/mobile-v2/immich_lint/lib/immich_mobile_lint.dart new file mode 100644 index 0000000000..b4aa61ac56 --- /dev/null +++ b/mobile-v2/immich_lint/lib/immich_mobile_lint.dart @@ -0,0 +1,99 @@ +import 'package:analyzer/error/error.dart' show ErrorSeverity; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; +// ignore: depend_on_referenced_packages +import 'package:glob/glob.dart'; + +PluginBase createPlugin() => ImLinter(); + +class ImLinter extends PluginBase { + @override + List getLintRules(CustomLintConfigs configs) { + final List rules = []; + for (final entry in configs.rules.entries) { + if (entry.value.enabled && entry.key.startsWith("import_rule_")) { + final code = makeCode(entry.key, entry.value); + final allowedPaths = getStrings(entry.value, "allowed"); + final forbiddenPaths = getStrings(entry.value, "forbidden"); + final restrict = getStrings(entry.value, "restrict"); + rules.add(ImportRule(code, buildGlob(allowedPaths), + buildGlob(forbiddenPaths), restrict)); + } + } + return rules; + } + + static makeCode(String name, LintOptions options) => LintCode( + name: name, + problemMessage: options.json["message"] as String, + errorSeverity: ErrorSeverity.WARNING, + ); + + static List getStrings(LintOptions options, String field) { + final List result = []; + final excludeOption = options.json[field]; + if (excludeOption is String) { + result.add(excludeOption); + } else if (excludeOption is List) { + result.addAll(excludeOption.map((option) => option)); + } + return result; + } + + Glob? buildGlob(List globs) { + if (globs.isEmpty) return null; + if (globs.length == 1) return Glob(globs[0], caseSensitive: true); + return Glob("{${globs.join(",")}}", caseSensitive: true); + } +} + +// ignore: must_be_immutable +class ImportRule extends DartLintRule { + ImportRule(LintCode code, this._allowed, this._forbidden, this._restrict) + : super(code: code); + + final Glob? _allowed; + final Glob? _forbidden; + final List _restrict; + int rootOffset = -1; + + @override + Future startUp( + CustomLintResolver resolver, + CustomLintContext context, + ) async { + if (rootOffset == -1) { + rootOffset = resolver.path.toLowerCase().lastIndexOf("immich/"); + rootOffset = resolver.path.indexOf("lib/", rootOffset); + } + + return super.startUp(resolver, context); + } + + @override + void run( + CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context, + ) { + if (rootOffset == -1) { + print("Cannot find project root. Skipping ${resolver.path}"); + return; + } + + final path = resolver.path.substring(rootOffset); + if ((_allowed != null && _allowed!.matches(path)) && + (_forbidden == null || !_forbidden!.matches(path))) return; + + context.registry.addImportDirective((node) { + final uri = node.uri.stringValue; + if (uri == null) return; + for (final restricted in _restrict) { + if (uri.startsWith(restricted) == true) { + reporter.atNode(node, code); + return; + } + } + }); + } +} diff --git a/mobile-v2/immich_lint/pubspec.lock b/mobile-v2/immich_lint/pubspec.lock new file mode 100644 index 0000000000..6f3561b71e --- /dev/null +++ b/mobile-v2/immich_lint/pubspec.lock @@ -0,0 +1,370 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77" + url: "https://pub.dev" + source: hosted + version: "73.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" + analyzer: + dependency: "direct main" + description: + name: analyzer + sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a" + url: "https://pub.dev" + source: hosted + version: "6.8.0" + analyzer_plugin: + dependency: "direct main" + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" + args: + dependency: transitive + description: + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" + source: hosted + version: "2.6.0" + async: + dependency: transitive + description: + name: async + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + url: "https://pub.dev" + source: hosted + version: "2.12.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" + collection: + dependency: transitive + description: + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + url: "https://pub.dev" + source: hosted + version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + custom_lint: + dependency: transitive + description: + name: custom_lint + sha256: "832fcdc676171205201c9cffafd6b5add19393962f6598af8472b48b413026e6" + url: "https://pub.dev" + source: hosted + version: "0.6.8" + custom_lint_builder: + dependency: "direct main" + description: + name: custom_lint_builder + sha256: c3d82779026f91b8e00c9ac18934595cbc9b490094ea682052beeafdb2bd50ac + url: "https://pub.dev" + source: hosted + version: "0.6.8" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "4ddbbdaa774265de44c97054dcec058a83d9081d071785ece601e348c18c267d" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" + url: "https://pub.dev" + source: hosted + version: "2.3.7" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + glob: + dependency: "direct main" + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + lints: + dependency: "direct dev" + description: + name: lints + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + url: "https://pub.dev" + source: hosted + version: "0.7.3" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + url: "https://pub.dev" + source: hosted + version: "14.3.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.0 <4.0.0" diff --git a/mobile-v2/immich_lint/pubspec.yaml b/mobile-v2/immich_lint/pubspec.yaml new file mode 100644 index 0000000000..3dc564240d --- /dev/null +++ b/mobile-v2/immich_lint/pubspec.yaml @@ -0,0 +1,14 @@ +name: immich_mobile_lint +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dependencies: + analyzer: ^6.7.0 + analyzer_plugin: ^0.11.3 + custom_lint_builder: ^0.6.4 + glob: ^2.1.2 + +dev_dependencies: + lints: ^5.0.0 diff --git a/mobile-v2/lib/main.dart b/mobile-v2/lib/main.dart index 8fe8398ff8..b4f5c64caf 100644 --- a/mobile-v2/lib/main.dart +++ b/mobile-v2/lib/main.dart @@ -3,6 +3,7 @@ import 'package:immich_mobile/i18n/strings.g.dart'; import 'package:immich_mobile/immich_app.dart'; import 'package:immich_mobile/service_locator.dart'; import 'package:immich_mobile/utils/log_manager.dart'; +// ignore: import_rule_photo_manager import 'package:photo_manager/photo_manager.dart'; void main() { diff --git a/mobile-v2/pubspec.lock b/mobile-v2/pubspec.lock index 1f6d100d17..1cfffdb000 100644 --- a/mobile-v2/pubspec.lock +++ b/mobile-v2/pubspec.lock @@ -190,6 +190,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" cli_util: dependency: transitive description: @@ -254,6 +262,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: "832fcdc676171205201c9cffafd6b5add19393962f6598af8472b48b413026e6" + url: "https://pub.dev" + source: hosted + version: "0.6.8" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: c3d82779026f91b8e00c9ac18934595cbc9b490094ea682052beeafdb2bd50ac + url: "https://pub.dev" + source: hosted + version: "0.6.8" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "4ddbbdaa774265de44c97054dcec058a83d9081d071785ece601e348c18c267d" + url: "https://pub.dev" + source: hosted + version: "0.6.5" dart_style: dependency: transitive description: @@ -450,6 +482,14 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" frontend_server_client: dependency: transitive description: @@ -490,6 +530,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" http: dependency: "direct main" description: @@ -522,6 +570,13 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + immich_mobile_lint: + dependency: "direct dev" + description: + path: immich_lint + relative: true + source: path + version: "0.0.0" intl: dependency: "direct main" description: diff --git a/mobile-v2/pubspec.yaml b/mobile-v2/pubspec.yaml index 633e559ee4..4754def389 100644 --- a/mobile-v2/pubspec.yaml +++ b/mobile-v2/pubspec.yaml @@ -76,6 +76,10 @@ dev_dependencies: flutter_gen_runner: ^5.8.0 # Type safe platform channels pigeon: ^22.5.0 + # Custom lint + custom_lint: ^0.6.4 + immich_mobile_lint: + path: './immich_lint' flutter: uses-material-design: true