From fd7b06c15d1e46131776fbae61b291a01d910a2a Mon Sep 17 00:00:00 2001 From: fusion44 Date: Wed, 1 Nov 2023 20:22:48 +0100 Subject: [PATCH 01/18] feat: add initial version of the analyzer plugin --- packages/jaspr_lints/.gitignore | 7 ++ packages/jaspr_lints/CHANGELOG.md | 3 + packages/jaspr_lints/README.md | 31 +++++++ packages/jaspr_lints/analysis_options.yaml | 30 +++++++ packages/jaspr_lints/lib/jaspr_lints.dart | 90 +++++++++++++++++++++ packages/jaspr_lints/pubspec.yaml | 17 ++++ packages/jaspr_lints/screenshots/1.png | Bin 0 -> 91471 bytes 7 files changed, 178 insertions(+) create mode 100644 packages/jaspr_lints/.gitignore create mode 100644 packages/jaspr_lints/CHANGELOG.md create mode 100644 packages/jaspr_lints/README.md create mode 100644 packages/jaspr_lints/analysis_options.yaml create mode 100644 packages/jaspr_lints/lib/jaspr_lints.dart create mode 100644 packages/jaspr_lints/pubspec.yaml create mode 100644 packages/jaspr_lints/screenshots/1.png diff --git a/packages/jaspr_lints/.gitignore b/packages/jaspr_lints/.gitignore new file mode 100644 index 00000000..3cceda55 --- /dev/null +++ b/packages/jaspr_lints/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/jaspr_lints/CHANGELOG.md b/packages/jaspr_lints/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/packages/jaspr_lints/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/packages/jaspr_lints/README.md b/packages/jaspr_lints/README.md new file mode 100644 index 00000000..df1fd3b7 --- /dev/null +++ b/packages/jaspr_lints/README.md @@ -0,0 +1,31 @@ +# Jaspr Lints + +This package provides lints and code assists for users of the Jaspr framework. + +### Features: +* Remove components from the compent tree +* Add components tp the component tree +* More to come + +### Setup: + +* The application must contain an `analysis_options.yaml` with the following: +```yaml +analyzer: + plugins: + - custom_lint +``` + +* Add `custom_lint` and `jaspr_lints` as dev dependency: +```yaml +name: example_app +environment: + sdk: ">=2.16.0 <3.0.0" + +dev_dependencies: + custom_lint: + jaspr_lints: +``` +After running pub get you now see additional Jaspr lints when invoking code assist on a component function like `div()` or `p()` files: + +![Example screenshot of what Jaspr code assists look like](screenshots/1.png "Jaspr Code Assists") diff --git a/packages/jaspr_lints/analysis_options.yaml b/packages/jaspr_lints/analysis_options.yaml new file mode 100644 index 00000000..dee8927a --- /dev/null +++ b/packages/jaspr_lints/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart new file mode 100644 index 00000000..5c4101d2 --- /dev/null +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -0,0 +1,90 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +final funcs = ['p', 'text', 'div', 'img', 'small']; + +PluginBase createPlugin() => _JasprLinter(); + +class _JasprLinter extends PluginBase { + @override + List getLintRules(CustomLintConfigs configs) => []; + + @override + List getAssists() => [_JasprFunctionAssistProvider()]; +} + +class _JasprFunctionAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addMethodInvocation((node) { + final method = node.methodName.toString(); + + if (target.length != 0) return; + if (target.offset < node.offset) return; + if (target.offset > node.offset + method.length) return; + + if (method == 'tw') { + // Do Tailwind formatting + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Sort Tailwind classes', + ); + + cb.addDartFileEdit((builder) {}); + } + + if (funcs.contains(method)) { + _wrapWithComponent(reporter, node); + _removeComponent(reporter, node); + } + }); + } + + void _wrapWithComponent(ChangeReporter reporter, MethodInvocation node) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Wrap with Component', + ); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + editBuilder.write( + 'div([${node.toSource()}])', + ); + }); + }); + } + + void _removeComponent(ChangeReporter reporter, MethodInvocation node) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Remove Component', + ); + ListLiteral? children; + for (final a in node.argumentList.arguments) { + if (a is ListLiteral) { + children = a; + break; + } + } + + if (children == null) return; + + final newSource = children.childEntities + .toList() + .sublist(1, children.childEntities.length - 1) + .join(','); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + editBuilder.write(newSource); + }); + }); + } +} diff --git a/packages/jaspr_lints/pubspec.yaml b/packages/jaspr_lints/pubspec.yaml new file mode 100644 index 00000000..2454e60a --- /dev/null +++ b/packages/jaspr_lints/pubspec.yaml @@ -0,0 +1,17 @@ +name: jaspr_lints +description: A customer linter package for the Jaspr framework +version: 0.1.0 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ^3.1.3 + +# Add regular dependencies here. +dependencies: + analyzer: ^5.4.0 + analyzer_plugin: ^0.11.0 + custom_lint_builder: ^0.5.5 + +dev_dependencies: + lints: ^3.0.0 + test: ^1.21.0 diff --git a/packages/jaspr_lints/screenshots/1.png b/packages/jaspr_lints/screenshots/1.png new file mode 100644 index 0000000000000000000000000000000000000000..cfdf960c2a61aabbde885c2477f0f93e07d966f7 GIT binary patch literal 91471 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 Date: Tue, 3 Sep 2024 12:30:45 +0200 Subject: [PATCH 02/18] update jaspr_lints plugin and add new component assists --- packages/jaspr_lints/README.md | 26 ++-- packages/jaspr_lints/lib/jaspr_lints.dart | 91 ++------------ .../lib/src/assists/component_assist.dart | 115 ++++++++++++++++++ .../lib/src/assists/tailwind_assist.dart | 30 +++++ .../lib/src/assists/tree_assist.dart | 64 ++++++++++ packages/jaspr_lints/pubspec.yaml | 23 ++-- .../tools/analyzer_plugin/bin/plugin.dart | 7 ++ .../tools/analyzer_plugin/pubspec.yaml | 10 ++ 8 files changed, 262 insertions(+), 104 deletions(-) create mode 100644 packages/jaspr_lints/lib/src/assists/component_assist.dart create mode 100644 packages/jaspr_lints/lib/src/assists/tailwind_assist.dart create mode 100644 packages/jaspr_lints/lib/src/assists/tree_assist.dart create mode 100644 packages/jaspr_lints/tools/analyzer_plugin/bin/plugin.dart create mode 100644 packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml diff --git a/packages/jaspr_lints/README.md b/packages/jaspr_lints/README.md index df1fd3b7..b45ef64c 100644 --- a/packages/jaspr_lints/README.md +++ b/packages/jaspr_lints/README.md @@ -1,31 +1,27 @@ # Jaspr Lints -This package provides lints and code assists for users of the Jaspr framework. +This package provides lints and code assists for jaspr projects. ### Features: -* Remove components from the compent tree +* Create a new StatelessComponent / StatefulComponent / InheritedComponent +* Remove components from the component tree * Add components tp the component tree * More to come ### Setup: -* The application must contain an `analysis_options.yaml` with the following: +* Add `jaspr_lints` as dev dependency: +```shell +dart pub add jaspr_lints --dev +``` + +* Add the following to your `analysis_options.yaml` file: ```yaml analyzer: plugins: - - custom_lint + - jaspr_lints ``` -* Add `custom_lint` and `jaspr_lints` as dev dependency: -```yaml -name: example_app -environment: - sdk: ">=2.16.0 <3.0.0" - -dev_dependencies: - custom_lint: - jaspr_lints: -``` -After running pub get you now see additional Jaspr lints when invoking code assist on a component function like `div()` or `p()` files: +After running `pub get` you now see additional Jaspr lints when invoking code assist on a component function like `div()` or `p()` files: ![Example screenshot of what Jaspr code assists look like](screenshots/1.png "Jaspr Code Assists") diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index 5c4101d2..a53e7109 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,90 +1,19 @@ -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/source/source_range.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; -final funcs = ['p', 'text', 'div', 'img', 'small']; +import 'src/assists/component_assist.dart'; +import 'src/assists/tailwind_assist.dart'; +import 'src/assists/tree_assist.dart'; -PluginBase createPlugin() => _JasprLinter(); +PluginBase createPlugin() => JasprLinter(); -class _JasprLinter extends PluginBase { +class JasprLinter extends PluginBase { @override List getLintRules(CustomLintConfigs configs) => []; @override - List getAssists() => [_JasprFunctionAssistProvider()]; -} - -class _JasprFunctionAssistProvider extends DartAssist { - @override - void run( - CustomLintResolver resolver, - ChangeReporter reporter, - CustomLintContext context, - SourceRange target, - ) { - context.registry.addMethodInvocation((node) { - final method = node.methodName.toString(); - - if (target.length != 0) return; - if (target.offset < node.offset) return; - if (target.offset > node.offset + method.length) return; - - if (method == 'tw') { - // Do Tailwind formatting - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Sort Tailwind classes', - ); - - cb.addDartFileEdit((builder) {}); - } - - if (funcs.contains(method)) { - _wrapWithComponent(reporter, node); - _removeComponent(reporter, node); - } - }); - } - - void _wrapWithComponent(ChangeReporter reporter, MethodInvocation node) { - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Wrap with Component', - ); - - cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { - editBuilder.write( - 'div([${node.toSource()}])', - ); - }); - }); - } - - void _removeComponent(ChangeReporter reporter, MethodInvocation node) { - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Remove Component', - ); - ListLiteral? children; - for (final a in node.argumentList.arguments) { - if (a is ListLiteral) { - children = a; - break; - } - } - - if (children == null) return; - - final newSource = children.childEntities - .toList() - .sublist(1, children.childEntities.length - 1) - .join(','); - - cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { - editBuilder.write(newSource); - }); - }); - } + List getAssists() => [ + ComponentAssistProvider(), + TreeAssistProvider(), + TailwindAssistProvider(), + ]; } diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart new file mode 100644 index 00000000..62957bfd --- /dev/null +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -0,0 +1,115 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +class ComponentAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addCompilationUnit((node) { + var hasJasprImport = false; + + for (var dir in node.directives) { + if (dir.offset > target.end) { + return; + } + if (dir is ImportDirective && (dir.uri.stringValue?.startsWith('package:jaspr/') ?? false)) { + hasJasprImport = true; + } + } + + for (var dec in node.declarations) { + if (target.offset >= dec.offset && target.end <= dec.end) { + return; + } + } + + var nameSuggestion = resolver.path.split('/').last; + if (nameSuggestion.endsWith('.dart')) nameSuggestion = nameSuggestion.substring(0, nameSuggestion.length - 5); + + nameSuggestion = nameSuggestion.split('_').map((s) => s.substring(0, 1).toUpperCase() + s.substring(1)).join(); + + reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends StatelessComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.key});\n\n @override\n Iterable build(BuildContext context) sync* {\n' + ' yield '); + edit.addSimpleLinkedEdit('child', "div([])"); + edit.write(';\n }\n}\n'); + }); + + if (!hasJasprImport) { + builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { + edit.write("\nimport 'package:jaspr/jaspr.dart';"); + }); + } + }); + + reporter.createChangeBuilder(priority: 2, message: 'Create StatefulComponent').addDartFileEdit((builder) { + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends StatefulComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.key});\n\n @override\n State createState() => '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('State();\n}\n\nclass '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('State extends State<'); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('> {\n\n @override\n Iterable build(BuildContext context) sync* {\n' + ' yield '); + edit.addSimpleLinkedEdit('child', "div([])"); + edit.write(';\n }\n}\n'); + }); + + if (!hasJasprImport) { + builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { + edit.write("\nimport 'package:jaspr/jaspr.dart';"); + }); + } + }); + + reporter.createChangeBuilder(priority: 3, message: 'Create InheritedComponent').addDartFileEdit((builder) { + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends InheritedComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.child, super.children, super.key});\n\n static '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' of(BuildContext context) {\n final '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('? result = context.dependOnInheritedComponentOfExactType<'); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('>();\n assert(result != null, \'No '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write( + ' found in context.\');\n return result!;\n }\n\n @override\n bool updateShouldNotify(covariant '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' oldComponent) {\n return false;\n }\n}\n'); + }); + + if (!hasJasprImport) { + builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { + edit.write("\nimport 'package:jaspr/jaspr.dart';"); + }); + } + }); + }); + } +} diff --git a/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart b/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart new file mode 100644 index 00000000..4d604727 --- /dev/null +++ b/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart @@ -0,0 +1,30 @@ +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +class TailwindAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addMethodInvocation((node) { + final method = node.methodName.toString(); + + if (target.length != 0) return; + if (target.offset < node.offset) return; + if (target.offset > node.offset + method.length) return; + + if (method == 'tw') { + // Do Tailwind formatting + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Sort Tailwind classes', + ); + + cb.addDartFileEdit((builder) {}); + } + }); + } +} diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart new file mode 100644 index 00000000..df9af9fd --- /dev/null +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -0,0 +1,64 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +final funcs = ['p', 'text', 'div', 'img', 'small']; + +class TreeAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addMethodInvocation((node) { + final method = node.methodName.toString(); + + if (funcs.contains(method)) { + _wrapWithComponent(reporter, node); + _removeComponent(reporter, node); + } + }); + } + + void _wrapWithComponent(ChangeReporter reporter, MethodInvocation node) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Wrap with Component', + ); + + cb.addDartFileEdit((builder) { + builder.addInsertion(node.offset, (editBuilder) { + editBuilder.write('div([\n'); + }); + builder.addInsertion(node.offset + node.length, (editBuilder) { + editBuilder.write('\n])'); + }); + }); + } + + void _removeComponent(ChangeReporter reporter, MethodInvocation node) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Remove Component', + ); + ListLiteral? children; + for (final a in node.argumentList.arguments) { + if (a is ListLiteral) { + children = a; + break; + } + } + + if (children == null) return; + + final newSource = children.childEntities.toList().sublist(1, children.childEntities.length - 1).join(','); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + editBuilder.write(newSource); + }); + }); + } +} diff --git a/packages/jaspr_lints/pubspec.yaml b/packages/jaspr_lints/pubspec.yaml index 2454e60a..ef9b610d 100644 --- a/packages/jaspr_lints/pubspec.yaml +++ b/packages/jaspr_lints/pubspec.yaml @@ -1,17 +1,24 @@ name: jaspr_lints -description: A customer linter package for the Jaspr framework -version: 0.1.0 -# repository: https://github.com/my_org/my_repo +description: A collection of lints and assists for jaspr projects. +version: 0.1.0 +homepage: https://github.com/schultek/jaspr +issue_tracker: https://github.com/schultek/jaspr/issues +documentation: https://docs.page/schultek/jaspr +funding: + - https://github.com/sponsors/schultek + +topics: + - jaspr + - web + - lints environment: - sdk: ^3.1.3 + sdk: '>=3.0.0 <4.0.0' -# Add regular dependencies here. dependencies: - analyzer: ^5.4.0 + analyzer: ^6.5.0 analyzer_plugin: ^0.11.0 - custom_lint_builder: ^0.5.5 + custom_lint_builder: ^0.6.5 dev_dependencies: lints: ^3.0.0 - test: ^1.21.0 diff --git a/packages/jaspr_lints/tools/analyzer_plugin/bin/plugin.dart b/packages/jaspr_lints/tools/analyzer_plugin/bin/plugin.dart new file mode 100644 index 00000000..1fe26329 --- /dev/null +++ b/packages/jaspr_lints/tools/analyzer_plugin/bin/plugin.dart @@ -0,0 +1,7 @@ +import 'dart:isolate'; + +import 'package:custom_lint/src/analyzer_plugin_starter.dart'; + +Future main(List args, SendPort sendPort) async { + await start(args, sendPort); +} diff --git a/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml b/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml new file mode 100644 index 00000000..70a03d39 --- /dev/null +++ b/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml @@ -0,0 +1,10 @@ +name: jaspr_lints_analyzer_plugin_loader +description: This pubspec determines the version of the analyzer plugin to load. +version: 0.1.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + custom_lint: 0.6.5 From d77cfb575cefb05613d952629e7e949a9ee1b960 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Sun, 8 Sep 2024 14:17:59 +0200 Subject: [PATCH 03/18] add code assists for component tree manipulation --- packages/jaspr_lints/CHANGELOG.md | 2 +- .../lib/src/assists/component_assist.dart | 102 ++++++++-- .../lib/src/assists/tree_assist.dart | 176 ++++++++++++++---- 3 files changed, 229 insertions(+), 51 deletions(-) diff --git a/packages/jaspr_lints/CHANGELOG.md b/packages/jaspr_lints/CHANGELOG.md index effe43c8..a0712a79 100644 --- a/packages/jaspr_lints/CHANGELOG.md +++ b/packages/jaspr_lints/CHANGELOG.md @@ -1,3 +1,3 @@ -## 1.0.0 +## 0.1.0 - Initial version. diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index 62957bfd..12306246 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -1,5 +1,7 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; class ComponentAssistProvider extends DartAssist { @@ -34,6 +36,9 @@ class ComponentAssistProvider extends DartAssist { nameSuggestion = nameSuggestion.split('_').map((s) => s.substring(0, 1).toUpperCase() + s.substring(1)).join(); reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { + if (!hasJasprImport) { + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); + } builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); edit.addSimpleLinkedEdit('name', nameSuggestion); @@ -46,15 +51,12 @@ class ComponentAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('child', "div([])"); edit.write(';\n }\n}\n'); }); - - if (!hasJasprImport) { - builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { - edit.write("\nimport 'package:jaspr/jaspr.dart';"); - }); - } }); reporter.createChangeBuilder(priority: 2, message: 'Create StatefulComponent').addDartFileEdit((builder) { + if (!hasJasprImport) { + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); + } builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); edit.addSimpleLinkedEdit('name', nameSuggestion); @@ -73,15 +75,13 @@ class ComponentAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('child', "div([])"); edit.write(';\n }\n}\n'); }); + }); + reporter.createChangeBuilder(priority: 3, message: 'Create InheritedComponent').addDartFileEdit((builder) { if (!hasJasprImport) { - builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { - edit.write("\nimport 'package:jaspr/jaspr.dart';"); - }); + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); } - }); - reporter.createChangeBuilder(priority: 3, message: 'Create InheritedComponent').addDartFileEdit((builder) { builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); edit.addSimpleLinkedEdit('name', nameSuggestion); @@ -103,13 +103,85 @@ class ComponentAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('name', nameSuggestion); edit.write(' oldComponent) {\n return false;\n }\n}\n'); }); + }); + }); - if (!hasJasprImport) { - builder.addInsertion(node.directives.lastOrNull?.end ?? 0, (edit) { - edit.write("\nimport 'package:jaspr/jaspr.dart';"); - }); + context.registry.addClassDeclaration((node) { + if (target.offset < node.offset || target.end > node.leftBracket.end) { + return; + } + if (node.extendsClause?.superclass.name2.lexeme != 'StatelessComponent') { + return; + } + + MethodDeclaration? buildMethod; + List members = []; + for (var m in node.members) { + if (m is MethodDeclaration && m.name.lexeme == "build") { + buildMethod = m; + } else if (m is FieldDeclaration) { + members.addAll(m.fields.variables.map((v) => v.name.lexeme)); } + } + + reporter.createChangeBuilder(priority: 1, message: 'Convert to StatefulComponent').addDartFileEdit((builder) { + builder.addReplacement(node.extendsClause!.superclass.sourceRange, (edit) { + edit.write('StatefulComponent'); + }); + + var splitToken = buildMethod?.beginToken ?? node.rightBracket; + var indent = buildMethod != null ? '' : ' '; + var endIndent = buildMethod != null ? ' ' : ''; + var name = node.name.lexeme; + + builder.addInsertion(splitToken.offset, (edit) { + edit.write("${indent}@override\n State createState() => ${name}State();\n" + "}\n\nclass ${name}State extends State<$name> {\n$endIndent"); + }); + + buildMethod?.body.visitChildren(StateBuildVisitor(builder, node)); }); + + if (buildMethod != null && buildMethod.body.keyword != null) { + reporter + .createChangeBuilder(priority: 1, message: 'Convert to AsyncStatelessComponent') + .addDartFileEdit((builder) { + if (node.parent case CompilationUnit unit) { + for (var dir in unit.directives) { + if (dir is ImportDirective && dir.uri.stringValue == 'package:jaspr/jaspr.dart') { + builder.addDeletion(dir.sourceRange); + } + } + } + + builder.importLibrary(Uri.parse('package:jaspr/server.dart')); + + builder.addSimpleReplacement(node.extendsClause!.superclass.sourceRange, 'AsyncStatelessComponent'); + builder.addSimpleReplacement(buildMethod!.body.keyword!.sourceRange, 'async'); + if (buildMethod.returnType != null) { + builder.addSimpleReplacement(buildMethod.returnType!.sourceRange, 'Stream'); + } else { + builder.addSimpleInsertion(buildMethod.name.offset, 'Stream '); + } + }); + } }); } } + +class StateBuildVisitor extends UnifyingAstVisitor { + StateBuildVisitor(this.builder, this.clazz); + + final DartFileEditBuilder builder; + final ClassDeclaration clazz; + + @override + visitSimpleIdentifier(SimpleIdentifier node) { + var elem = node.staticElement; + if (elem == null) return; + + if (elem.enclosingElement == clazz.declaredElement) { + builder.addSimpleInsertion(node.offset, 'component.'); + } + } +} diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index df9af9fd..b46ae458 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -1,8 +1,8 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; - -final funcs = ['p', 'text', 'div', 'img', 'small']; +import 'package:jaspr_lints/src/utils.dart'; class TreeAssistProvider extends DartAssist { @override @@ -12,53 +12,159 @@ class TreeAssistProvider extends DartAssist { CustomLintContext context, SourceRange target, ) { - context.registry.addMethodInvocation((node) { - final method = node.methodName.toString(); - - if (funcs.contains(method)) { - _wrapWithComponent(reporter, node); - _removeComponent(reporter, node); + context.registry.addInvocationExpression((node) { + if (!target.coveredBy(node.function.sourceRange)) { + return; } + if (!isComponentType(node.staticType)) { + return; + } + final indent = getLineIndent(resolver.lineInfo, node); + wrapComponent(resolver, reporter, node, indent); + removeComponent(resolver, reporter, node, indent, node.argumentList); + }); + context.registry.addInstanceCreationExpression((node) { + if (!target.coveredBy(node.constructorName.sourceRange)) { + return; + } + if (!isComponentType(node.staticType)) { + return; + } + final indent = getLineIndent(resolver.lineInfo, node); + wrapComponent(resolver, reporter, node, indent); + removeComponent(resolver, reporter, node, indent, node.argumentList); }); } - void _wrapWithComponent(ChangeReporter reporter, MethodInvocation node) { - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Wrap with Component', - ); + void wrapComponent(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent) { + var content = resolver.source.contents.data; + var lines = content.substring(node.offset, node.end).split('\n'); - cb.addDartFileEdit((builder) { - builder.addInsertion(node.offset, (editBuilder) { - editBuilder.write('div([\n'); + var htmlSource = + '([\n${''.padLeft(lineIndent)}${lines.map((s) => ' ' + s).join('\n')},\n${''.padLeft(lineIndent)}])'; + + void wrapWith(String name, [List? suggestions]) { + final cb = reporter.createChangeBuilder( + priority: 2, + message: 'Wrap with $name', + ); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + if (suggestions != null) { + editBuilder.addSimpleLinkedEdit(name, suggestions.first, + kind: LinkedEditSuggestionKind.METHOD, suggestions: suggestions); + } else { + editBuilder.write(name); + } + editBuilder.write(htmlSource); + }); }); - builder.addInsertion(node.offset + node.length, (editBuilder) { - editBuilder.write('\n])'); + } + + wrapWith('html...', ['div', 'section', 'ul', 'li', 'h1', 'p', 'span', 'button']); + wrapWith('div'); + wrapWith('section'); + wrapWith('ul'); + wrapWith('li'); + + final cb2 = reporter.createChangeBuilder( + priority: 2, + message: 'Wrap with component...', + ); + + cb2.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + editBuilder.addSimpleLinkedEdit('comp', 'component'); + editBuilder.write('(\n'); + editBuilder.write(''.padLeft(lineIndent, ' ')); + editBuilder.write(' child: '); + editBuilder.writeln(lines[0]); + editBuilder.write(lines.skip(1).map((s) => ' ' + s).join('\n')); + editBuilder.write(',\n${''.padLeft(lineIndent, ' ')})'); }); }); } - void _removeComponent(ChangeReporter reporter, MethodInvocation node) { - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Remove Component', - ); - ListLiteral? children; - for (final a in node.argumentList.arguments) { - if (a is ListLiteral) { - children = a; - break; - } + void removeComponent(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent, + ArgumentList argumentList) { + var children = [ + for (var arg in argumentList.arguments) + if (arg is NamedExpression) + if (arg.name.label.name == 'child' && isComponentType(arg.staticType)) + arg.expression + else if (arg.name.label.name == 'children' && arg.expression is ListLiteral) + ...(arg.expression as ListLiteral).elements + else + ...[] + else if (arg is ListLiteral) + ...arg.elements, + ]; + + if (children.isEmpty) { + return; } - if (children == null) return; + var content = resolver.source.contents.data; - final newSource = children.childEntities.toList().sublist(1, children.childEntities.length - 1).join(','); + if (children.length == 1) { + final cb = reporter.createChangeBuilder( + priority: 3, + message: 'Remove this component', + ); - cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { - editBuilder.write(newSource); + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + var child = children.first; + var childIndent = getLineIndent(resolver.lineInfo, child); + editBuilder + .write(content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true)); + }); }); - }); + } else if (node.parent is ListLiteral) { + final cb = reporter.createChangeBuilder( + priority: 3, + message: 'Remove this component', + ); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (editBuilder) { + for (var child in children) { + var childIndent = getLineIndent(resolver.lineInfo, child); + var source = content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true); + + if (child != children.first) { + editBuilder.write(''.padLeft(lineIndent)); + } + editBuilder.write(source); + if (child != children.last) { + editBuilder.write(',\n'); + } + } + }); + }); + } else if (node.parent is YieldStatement) { + final cb = reporter.createChangeBuilder( + priority: 3, + message: 'Remove this component', + ); + + cb.addDartFileEdit((builder) { + builder.addReplacement(node.parent!.sourceRange, (editBuilder) { + for (var child in children) { + var childIndent = getLineIndent(resolver.lineInfo, child); + var source = content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true); + + if (child != children.first) { + editBuilder.write(''.padLeft(lineIndent)); + } + editBuilder.write('yield $source;'); + if (child != children.last) { + editBuilder.write('\n'); + } + } + }); + }); + } } } From ed84b08df21d2fc23516e3643b393c07830d5c55 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Sun, 8 Sep 2024 14:18:23 +0200 Subject: [PATCH 04/18] add code assists for adding styles --- packages/jaspr_lints/lib/jaspr_lints.dart | 2 + .../lib/src/assists/css_assist.dart | 117 ++++++++++++++++++ packages/jaspr_lints/lib/src/utils.dart | 78 ++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 packages/jaspr_lints/lib/src/assists/css_assist.dart create mode 100644 packages/jaspr_lints/lib/src/utils.dart diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index a53e7109..f66803fa 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,4 +1,5 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:jaspr_lints/src/assists/css_assist.dart'; import 'src/assists/component_assist.dart'; import 'src/assists/tailwind_assist.dart'; @@ -15,5 +16,6 @@ class JasprLinter extends PluginBase { ComponentAssistProvider(), TreeAssistProvider(), TailwindAssistProvider(), + CssAssistProvider(), ]; } diff --git a/packages/jaspr_lints/lib/src/assists/css_assist.dart b/packages/jaspr_lints/lib/src/assists/css_assist.dart new file mode 100644 index 00000000..624ceab2 --- /dev/null +++ b/packages/jaspr_lints/lib/src/assists/css_assist.dart @@ -0,0 +1,117 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer_plugin/protocol/protocol_common.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +import '../utils.dart'; + +class CssAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addInvocationExpression((node) { + if (!target.coveredBy(node.function.sourceRange)) { + return; + } + if (!isComponentType(node.staticType)) { + return; + } + if (node.staticInvokeType case FunctionType t when hasClassesParameter(t.parameters)) { + final indent = getLineIndent(resolver.lineInfo, node); + addStyles(resolver, reporter, node, indent, node.argumentList); + } + }); + context.registry.addInstanceCreationExpression((node) { + if (!target.coveredBy(node.constructorName.sourceRange)) { + return; + } + if (!isComponentType(node.staticType)) { + return; + } + if (!hasClassesParameter(node.constructorName.staticElement?.parameters)) { + return; + } + final indent = getLineIndent(resolver.lineInfo, node); + addStyles(resolver, reporter, node, indent, node.argumentList); + }); + } + + void addStyles(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent, + ArgumentList argumentList) { + var comp = findParentComponent(node); + if (comp == null) { + return; + } + + var idArg = argumentList.arguments.whereType().where((e) => e.name.label.name == 'id').firstOrNull; + var idVal = idArg?.expression is StringLiteral ? (idArg!.expression as StringLiteral).stringValue : null; + + var classesArg = + argumentList.arguments.whereType().where((e) => e.name.label.name == 'classes').firstOrNull; + var classesVal = classesArg?.expression is StringLiteral + ? (classesArg!.expression as StringLiteral).stringValue?.split(' ').first + : null; + + var styles = comp.$1.members + .where((m) => m.metadata.where((a) => a.name.name == 'css').isNotEmpty) + .map((m) => switch (m) { + MethodDeclaration(body: BlockFunctionBody body) => + body.block.statements.whereType().firstOrNull?.expression, + MethodDeclaration(body: ExpressionFunctionBody body) => body.expression, + FieldDeclaration() => m.fields.variables.first.initializer, + _ => null, + }) + .whereType() + .firstOrNull; + + if (styles == null) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Add styles', + ); + + cb.addDartFileEdit((builder) { + builder.addInsertion(comp.$2.end, (edit) { + edit.write('\n\n @css\n static final List styles = [\n css(\''); + if (idVal != null) { + edit.write('#$idVal\').'); + } else if (classesVal != null) { + edit.write('.$classesVal\').'); + } else { + edit.write('.'); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write('\').'); + } + edit.addSimpleLinkedEdit('styles', 'box', + kind: LinkedEditSuggestionKind.METHOD, + suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); + edit.write('(),\n ];'); + }); + + if (idVal == null && classesVal == null) { + if (classesArg != null) { + builder.addInsertion(classesArg.expression.offset, (edit) { + edit.write("'"); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write(" \${"); + }); + builder.addInsertion(classesArg.expression.end, (edit) { + edit.write("}'"); + }); + } else { + builder.addInsertion(idArg?.end ?? argumentList.leftParenthesis.end, (edit) { + edit.write("classes: '"); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write("', "); + }); + } + } + }); + } else {} + } +} diff --git a/packages/jaspr_lints/lib/src/utils.dart b/packages/jaspr_lints/lib/src/utils.dart new file mode 100644 index 00000000..1e5ade25 --- /dev/null +++ b/packages/jaspr_lints/lib/src/utils.dart @@ -0,0 +1,78 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/token.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; +import 'package:analyzer/dart/element/type_visitor.dart'; +import 'package:analyzer/source/line_info.dart'; + +bool isComponentType(DartType? type) { + return type != null && type.accept(IsComponentVisitor()); +} + +int getLineIndent(LineInfo lineInfo, AstNode node) { + var lineNumber = lineInfo.getLocation(node.offset).lineNumber - 1; + var lineOffset = lineInfo.getOffsetOfLine(lineNumber); + Token token = node.beginToken; + while (token.previous != null && token.previous!.offset >= lineOffset) { + token = token.previous!; + } + return token.offset - lineOffset; +} + +bool hasClassesParameter(List? params) { + if (params == null) return false; + return params.where((p) => p.isNamed && p.name == 'classes' && p.type.isDartCoreString).isNotEmpty; +} + +(ClassDeclaration, MethodDeclaration)? findParentComponent(AstNode? node) { + if (node == null) { + return null; + } else if (node is MethodDeclaration) { + if (node.name.lexeme == 'build' && node.parent is ClassDeclaration) { + return (node.parent as ClassDeclaration, node); + } else { + return null; + } + } else { + return findParentComponent(node.parent); + } +} + +class IsComponentVisitor extends UnifyingTypeVisitor { + @override + bool visitDartType(DartType type) { + return false; + } + + @override + bool visitInterfaceType(InterfaceType type) { + var name = type.element.name; + var lib = type.element.library; + + if (lib.identifier == 'package:jaspr/src/framework/framework.dart') { + if (name == 'Component') return true; + } + if (type.superclass?.accept(this) ?? false) { + return true; + } + if (type.interfaces.any((t) => t.accept(this))) { + return true; + } + return false; + } +} + +extension Indent on String { + String reIndent(int delta, {bool skipFirst = false}) { + var lines = split('\n'); + for (var i = 0; i < lines.length; i++) { + if (i == 0 && skipFirst) continue; + if (delta > 0) { + lines[i] = ''.padLeft(delta) + lines[i]; + } else { + lines[i] = lines[i].substring(-delta); + } + } + return lines.join('\n'); + } +} From df94dfb31f0a314297934df2dd3b8c7f8d2948dc Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Sun, 8 Sep 2024 15:23:20 +0200 Subject: [PATCH 05/18] extend styles assist --- .../lib/src/assists/css_assist.dart | 112 +++++++++++++----- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/packages/jaspr_lints/lib/src/assists/css_assist.dart b/packages/jaspr_lints/lib/src/assists/css_assist.dart index 624ceab2..8b8de13b 100644 --- a/packages/jaspr_lints/lib/src/assists/css_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/css_assist.dart @@ -18,12 +18,16 @@ class CssAssistProvider extends DartAssist { if (!target.coveredBy(node.function.sourceRange)) { return; } - if (!isComponentType(node.staticType)) { - return; - } - if (node.staticInvokeType case FunctionType t when hasClassesParameter(t.parameters)) { - final indent = getLineIndent(resolver.lineInfo, node); - addStyles(resolver, reporter, node, indent, node.argumentList); + if (isComponentType(node.staticType)) { + if (node.staticInvokeType case FunctionType t when hasClassesParameter(t.parameters)) { + final indent = getLineIndent(resolver.lineInfo, node); + addStyles(resolver, reporter, node, indent, node.argumentList); + } + } else if (node is FunctionExpressionInvocation) { + if (node.function case SimpleIdentifier(name: 'css')) { + final indent = getLineIndent(resolver.lineInfo, node); + convertToNested(resolver, reporter, node, indent); + } } }); context.registry.addInstanceCreationExpression((node) { @@ -69,13 +73,12 @@ class CssAssistProvider extends DartAssist { .whereType() .firstOrNull; - if (styles == null) { - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Add styles', - ); - - cb.addDartFileEdit((builder) { + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Add styles', + ); + cb.addDartFileEdit((builder) { + if (styles == null) { builder.addInsertion(comp.$2.end, (edit) { edit.write('\n\n @css\n static final List styles = [\n css(\''); if (idVal != null) { @@ -92,26 +95,75 @@ class CssAssistProvider extends DartAssist { suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); edit.write('(),\n ];'); }); - - if (idVal == null && classesVal == null) { - if (classesArg != null) { - builder.addInsertion(classesArg.expression.offset, (edit) { - edit.write("'"); - edit.addSimpleLinkedEdit('className', 'classname'); - edit.write(" \${"); - }); - builder.addInsertion(classesArg.expression.end, (edit) { - edit.write("}'"); - }); + } else { + builder.addInsertion(styles.leftBracket.end, (edit) { + edit.write('\n css(\''); + if (idVal != null) { + edit.write('#$idVal\').'); + } else if (classesVal != null) { + edit.write('.$classesVal\').'); } else { - builder.addInsertion(idArg?.end ?? argumentList.leftParenthesis.end, (edit) { - edit.write("classes: '"); - edit.addSimpleLinkedEdit('className', 'classname'); - edit.write("', "); - }); + edit.write('.'); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write('\').'); } + edit.addSimpleLinkedEdit('styles', 'box', + kind: LinkedEditSuggestionKind.METHOD, + suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); + edit.write('(),'); + }); + } + + if (idVal == null && classesVal == null) { + if (classesArg != null) { + builder.addInsertion(classesArg.expression.offset, (edit) { + edit.write("'"); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write(" \${"); + }); + builder.addInsertion(classesArg.expression.end, (edit) { + edit.write("}'"); + }); + } else { + builder.addInsertion(idArg?.end ?? argumentList.leftParenthesis.end, (edit) { + edit.write("classes: '"); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write("', "); + }); } + } + }); + } + + void convertToNested( + CustomLintResolver resolver, ChangeReporter reporter, FunctionExpressionInvocation node, int lineIndent) { + if (node.argumentList.arguments.length != 1) { + return; + } + + var selector = node.argumentList.arguments.first; + var chain = getFullChain(node.parent); + + final cb = reporter.createChangeBuilder( + priority: 1, + message: 'Convert to nested styles', + ); + + cb.addDartFileEdit((builder) { + builder.addInsertion(selector.end, (edit) { + edit.write(', [\n${''.padLeft(lineIndent)} css(\'&\''); + }); + builder.addInsertion(chain?.end ?? node.end, (edit) { + edit.write(',\n${''.padLeft(lineIndent)}])'); }); - } else {} + }); + } + + MethodInvocation? getFullChain(AstNode? node) { + if (node is MethodInvocation) { + return getFullChain(node.parent) ?? node; + } else { + return null; + } } } From 560661364f348a04dd1916fac9fccb1e8835ed40 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Sun, 8 Sep 2024 21:48:37 +0200 Subject: [PATCH 06/18] add extract component assist --- .../lib/src/assists/component_assist.dart | 12 +-- .../lib/src/assists/css_assist.dart | 6 +- .../lib/src/assists/tree_assist.dart | 84 +++++++++++++------ 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index 12306246..c29b037e 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -36,9 +36,6 @@ class ComponentAssistProvider extends DartAssist { nameSuggestion = nameSuggestion.split('_').map((s) => s.substring(0, 1).toUpperCase() + s.substring(1)).join(); reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { - if (!hasJasprImport) { - builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); - } builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); edit.addSimpleLinkedEdit('name', nameSuggestion); @@ -51,12 +48,12 @@ class ComponentAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('child', "div([])"); edit.write(';\n }\n}\n'); }); + if (!hasJasprImport) { + builder.addSimpleInsertion(0, "import 'package:jaspr/jaspr.dart';\n"); + } }); reporter.createChangeBuilder(priority: 2, message: 'Create StatefulComponent').addDartFileEdit((builder) { - if (!hasJasprImport) { - builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); - } builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); edit.addSimpleLinkedEdit('name', nameSuggestion); @@ -75,6 +72,9 @@ class ComponentAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('child', "div([])"); edit.write(';\n }\n}\n'); }); + if (!hasJasprImport) { + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); + } }); reporter.createChangeBuilder(priority: 3, message: 'Create InheritedComponent').addDartFileEdit((builder) { diff --git a/packages/jaspr_lints/lib/src/assists/css_assist.dart b/packages/jaspr_lints/lib/src/assists/css_assist.dart index 8b8de13b..732f4379 100644 --- a/packages/jaspr_lints/lib/src/assists/css_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/css_assist.dart @@ -96,8 +96,8 @@ class CssAssistProvider extends DartAssist { edit.write('(),\n ];'); }); } else { - builder.addInsertion(styles.leftBracket.end, (edit) { - edit.write('\n css(\''); + builder.addInsertion(styles.rightBracket.offset, (edit) { + edit.write(' css(\''); if (idVal != null) { edit.write('#$idVal\').'); } else if (classesVal != null) { @@ -110,7 +110,7 @@ class CssAssistProvider extends DartAssist { edit.addSimpleLinkedEdit('styles', 'box', kind: LinkedEditSuggestionKind.METHOD, suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); - edit.write('(),'); + edit.write('(),\n '); }); } diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index b46ae458..3fecfcc6 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -22,6 +22,7 @@ class TreeAssistProvider extends DartAssist { final indent = getLineIndent(resolver.lineInfo, node); wrapComponent(resolver, reporter, node, indent); removeComponent(resolver, reporter, node, indent, node.argumentList); + extractComponent(resolver, reporter, node, indent); }); context.registry.addInstanceCreationExpression((node) { if (!target.coveredBy(node.constructorName.sourceRange)) { @@ -33,6 +34,7 @@ class TreeAssistProvider extends DartAssist { final indent = getLineIndent(resolver.lineInfo, node); wrapComponent(resolver, reporter, node, indent); removeComponent(resolver, reporter, node, indent, node.argumentList); + extractComponent(resolver, reporter, node, indent); }); } @@ -50,14 +52,14 @@ class TreeAssistProvider extends DartAssist { ); cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { + builder.addReplacement(node.sourceRange, (edit) { if (suggestions != null) { - editBuilder.addSimpleLinkedEdit(name, suggestions.first, + edit.addSimpleLinkedEdit(name, suggestions.first, kind: LinkedEditSuggestionKind.METHOD, suggestions: suggestions); } else { - editBuilder.write(name); + edit.write(name); } - editBuilder.write(htmlSource); + edit.write(htmlSource); }); }); } @@ -68,20 +70,20 @@ class TreeAssistProvider extends DartAssist { wrapWith('ul'); wrapWith('li'); - final cb2 = reporter.createChangeBuilder( + final cb = reporter.createChangeBuilder( priority: 2, message: 'Wrap with component...', ); - cb2.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { - editBuilder.addSimpleLinkedEdit('comp', 'component'); - editBuilder.write('(\n'); - editBuilder.write(''.padLeft(lineIndent, ' ')); - editBuilder.write(' child: '); - editBuilder.writeln(lines[0]); - editBuilder.write(lines.skip(1).map((s) => ' ' + s).join('\n')); - editBuilder.write(',\n${''.padLeft(lineIndent, ' ')})'); + cb.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (edit) { + edit.addSimpleLinkedEdit('comp', 'component'); + edit.write('(\n'); + edit.write(''.padLeft(lineIndent, ' ')); + edit.write(' child: '); + edit.writeln(lines[0]); + edit.write(lines.skip(1).map((s) => ' ' + s).join('\n')); + edit.write(',\n${''.padLeft(lineIndent, ' ')})'); }); }); } @@ -114,11 +116,10 @@ class TreeAssistProvider extends DartAssist { ); cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { + builder.addReplacement(node.sourceRange, (edit) { var child = children.first; var childIndent = getLineIndent(resolver.lineInfo, child); - editBuilder - .write(content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true)); + edit.write(content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true)); }); }); } else if (node.parent is ListLiteral) { @@ -128,17 +129,17 @@ class TreeAssistProvider extends DartAssist { ); cb.addDartFileEdit((builder) { - builder.addReplacement(node.sourceRange, (editBuilder) { + builder.addReplacement(node.sourceRange, (edit) { for (var child in children) { var childIndent = getLineIndent(resolver.lineInfo, child); var source = content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true); if (child != children.first) { - editBuilder.write(''.padLeft(lineIndent)); + edit.write(''.padLeft(lineIndent)); } - editBuilder.write(source); + edit.write(source); if (child != children.last) { - editBuilder.write(',\n'); + edit.write(',\n'); } } }); @@ -150,21 +151,54 @@ class TreeAssistProvider extends DartAssist { ); cb.addDartFileEdit((builder) { - builder.addReplacement(node.parent!.sourceRange, (editBuilder) { + builder.addReplacement(node.parent!.sourceRange, (edit) { for (var child in children) { var childIndent = getLineIndent(resolver.lineInfo, child); var source = content.substring(child.offset, child.end).reIndent(lineIndent - childIndent, skipFirst: true); if (child != children.first) { - editBuilder.write(''.padLeft(lineIndent)); + edit.write(''.padLeft(lineIndent)); } - editBuilder.write('yield $source;'); + edit.write('yield $source;'); if (child != children.last) { - editBuilder.write('\n'); + edit.write('\n'); } } }); }); } } + + void extractComponent(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent) { + var content = resolver.source.contents.data; + var source = content.substring(node.offset, node.end).reIndent(4 - lineIndent, skipFirst: true); + + final cb = reporter.createChangeBuilder( + priority: 4, + message: 'Extract component', + ); + + cb.addDartFileEdit((builder) { + builder.addInsertion(resolver.source.contents.data.length, (edit) { + edit.write('\nclass '); + edit.addSimpleLinkedEdit('name', 'MyComponent'); + edit.write(' extends StatelessComponent {\n' + ' const '); + edit.addSimpleLinkedEdit('name', 'MyComponent'); + edit.write('();\n' + '\n' + ' @override\n' + ' Iterable build(BuildContext context) sync* {\n' + ' yield '); + edit.write(source); + edit.write(';\n' + ' }\n}\n'); + }); + + builder.addReplacement(node.sourceRange, (edit) { + edit.addSimpleLinkedEdit('name', 'MyComponent'); + edit.write('()'); + }); + }); + } } From e41de82d13aeb5f290b92c724edfa22ae4588a8e Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Tue, 10 Sep 2024 17:19:48 +0200 Subject: [PATCH 07/18] add import assists --- packages/jaspr_lints/lib/jaspr_lints.dart | 2 + .../lib/src/assists/component_assist.dart | 2 +- .../lib/src/assists/import_assist.dart | 85 +++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 packages/jaspr_lints/lib/src/assists/import_assist.dart diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index f66803fa..6959e516 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,5 +1,6 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:jaspr_lints/src/assists/css_assist.dart'; +import 'package:jaspr_lints/src/assists/import_assist.dart'; import 'src/assists/component_assist.dart'; import 'src/assists/tailwind_assist.dart'; @@ -17,5 +18,6 @@ class JasprLinter extends PluginBase { TreeAssistProvider(), TailwindAssistProvider(), CssAssistProvider(), + ImportAssistProvider(), ]; } diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index c29b037e..f742483d 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -16,7 +16,7 @@ class ComponentAssistProvider extends DartAssist { var hasJasprImport = false; for (var dir in node.directives) { - if (dir.offset > target.end) { + if (dir.end > target.offset) { return; } if (dir is ImportDirective && (dir.uri.stringValue?.startsWith('package:jaspr/') ?? false)) { diff --git a/packages/jaspr_lints/lib/src/assists/import_assist.dart b/packages/jaspr_lints/lib/src/assists/import_assist.dart new file mode 100644 index 00000000..9550868b --- /dev/null +++ b/packages/jaspr_lints/lib/src/assists/import_assist.dart @@ -0,0 +1,85 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:analyzer/src/error/imports_verifier.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +class ImportAssistProvider extends DartAssist { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + SourceRange target, + ) { + context.registry.addImportDirective((node) { + if (!target.coveredBy(node.sourceRange)) { + return; + } + + var unit = node.parent; + if (unit is! CompilationUnit || unit.declaredElement == null) return; + + var fileName = resolver.source.shortName; + if (fileName.endsWith('.dart')) fileName = fileName.substring(0, fileName.length - 5); + + ImportDirective? importTarget = unit.directives + .whereType() + .where((d) => d.uri.stringValue == '$fileName.imports.dart') + .firstOrNull; + + var elements = []; + if (node.element case var libraryElement?) { + var visitor = GatherUsedImportedElementsVisitor(unit.declaredElement!.library); + unit.accept(visitor); + elements = visitor.usedElements.elements + .followedBy(visitor.usedElements.usedExtensions) + .where((e) => libraryElement.namespace.get(e.name ?? '') == e) + .toList(); + } + + var show = elements.map((e) => '#${e.name}').join(', '); + + void convertImport(String name) { + reporter + .createChangeBuilder(message: 'Convert to ${name.toLowerCase()}-only import', priority: 1) + .addDartFileEdit((builder) { + var annotation = '@Import.on$name(\'${node.uri.stringValue}\', show: [$show])\n'; + + if (importTarget != null) { + builder.addSimpleInsertion(importTarget.importKeyword.offset, annotation); + } else { + builder.addSimpleInsertion( + unit.directives.lastOrNull?.end ?? 0, + '\n${annotation}import \'$fileName.imports.dart\';', + ); + } + + unit.accept(ReplaceStubbedTypesVisitor(builder, elements)); + + builder.addDeletion(node.sourceRange); + }); + } + + convertImport('Web'); + convertImport('Server'); + }); + } +} + +class ReplaceStubbedTypesVisitor extends RecursiveAstVisitor { + ReplaceStubbedTypesVisitor(this.builder, this.elements); + + final DartFileEditBuilder builder; + final List elements; + + @override + void visitNamedType(NamedType node) { + if (elements.contains(node.type?.element)) { + if (node.parent is ConstructorName) return; + builder.addSimpleInsertion(node.name2.end, 'OrStubbed'); + } + } +} From 93581c28fdc01780febc1df5e66792e3e47ef01b Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 18:11:26 +0200 Subject: [PATCH 08/18] disable css rule in client mode --- .../jaspr_lints/lib/src/assists/css_assist.dart | 5 +++++ packages/jaspr_lints/lib/src/utils.dart | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/packages/jaspr_lints/lib/src/assists/css_assist.dart b/packages/jaspr_lints/lib/src/assists/css_assist.dart index 732f4379..e787b5d4 100644 --- a/packages/jaspr_lints/lib/src/assists/css_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/css_assist.dart @@ -14,6 +14,11 @@ class CssAssistProvider extends DartAssist { CustomLintContext context, SourceRange target, ) { + var config = readJasprConfig(resolver.path); + if (config['mode'] != 'server' && config['mode'] != 'static') { + return; + } + context.registry.addInvocationExpression((node) { if (!target.coveredBy(node.function.sourceRange)) { return; diff --git a/packages/jaspr_lints/lib/src/utils.dart b/packages/jaspr_lints/lib/src/utils.dart index 1e5ade25..0a2ac94f 100644 --- a/packages/jaspr_lints/lib/src/utils.dart +++ b/packages/jaspr_lints/lib/src/utils.dart @@ -1,9 +1,13 @@ +import 'dart:io'; + import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/dart/element/type_visitor.dart'; import 'package:analyzer/source/line_info.dart'; +import 'package:path/path.dart' as path; +import 'package:yaml/yaml.dart'; bool isComponentType(DartType? type) { return type != null && type.accept(IsComponentVisitor()); @@ -76,3 +80,15 @@ extension Indent on String { return lines.join('\n'); } } + +dynamic readJasprConfig(String filePath) { + var segments = path.split(filePath); + while (segments.length > 1) { + var pubspecFile = File(path.joinAll([...segments, 'pubspec.yaml'])); + if (pubspecFile.existsSync()) { + var pubspecData = loadYaml(pubspecFile.readAsStringSync()); + return pubspecData['jaspr']; + } + segments.removeLast(); + } +} From 5f81010a273f9d1e1d7f08f5d4a78716022388a0 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 19:01:54 +0200 Subject: [PATCH 09/18] add sort_children_last lint --- packages/jaspr_lints/lib/jaspr_lints.dart | 5 +- .../sort_children_properties_last_lint.dart | 67 +++++++++++++++++++ packages/jaspr_lints/lib/src/utils.dart | 6 ++ packages/jaspr_lints/pubspec.yaml | 2 + 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index 6959e516..8b011fc0 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,6 +1,7 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:jaspr_lints/src/assists/css_assist.dart'; import 'package:jaspr_lints/src/assists/import_assist.dart'; +import 'package:jaspr_lints/src/lints/sort_children_properties_last_lint.dart'; import 'src/assists/component_assist.dart'; import 'src/assists/tailwind_assist.dart'; @@ -10,7 +11,9 @@ PluginBase createPlugin() => JasprLinter(); class JasprLinter extends PluginBase { @override - List getLintRules(CustomLintConfigs configs) => []; + List getLintRules(CustomLintConfigs configs) => [ + SortChildrenPropertiesLastLint(), + ]; @override List getAssists() => [ diff --git a/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart b/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart new file mode 100644 index 00000000..40f46e48 --- /dev/null +++ b/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart @@ -0,0 +1,67 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart' show AnalysisError; +import 'package:analyzer/error/listener.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +import '../utils.dart'; + +class SortChildrenPropertiesLastLint extends DartLintRule { + SortChildrenPropertiesLastLint() + : super( + code: LintCode( + name: 'sort_children_properties_last', + problemMessage: 'Sort children properties last in html component methods.', + correctionMessage: 'This improves readability and best represents actual html.', + ), + ); + + @override + void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) { + context.registry.addInvocationExpression((node) { + if (!isComponentType(node.staticType)) { + return; + } + ListLiteral? childrenArg; + var violatesLint = false; + for (var argument in node.argumentList.arguments) { + if (argument is ListLiteral && isComponentListType(argument.staticType)) { + childrenArg = argument; + } else if (childrenArg != null && argument is NamedExpression) { + violatesLint = true; + } + } + if (violatesLint) { + reporter.atOffset(offset: node.offset, length: node.length, errorCode: code, data: (node, childrenArg)); + } + }); + } + + @override + List getFixes() => [SortChildrenPropertiesLastFix()]; +} + +class SortChildrenPropertiesLastFix extends DartFix { + @override + void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, AnalysisError analysisError, + List others) { + if (analysisError.errorCode.name == 'sort_children_properties_last') { + if (analysisError.data case (InvocationExpression node, ListLiteral childrenArg)) { + reporter.createChangeBuilder(message: 'Sort children last', priority: 2).addDartFileEdit((builder) { + var content = resolver.source.contents.data; + var lines = content.substring(childrenArg.offset, childrenArg.end); + + builder.addInsertion(node.argumentList.rightParenthesis.offset, (edit) { + if (node.argumentList.rightParenthesis.previous?.lexeme != ',') { + edit.write(', '); + } + edit.write(lines); + }); + + var nextArg = node.argumentList.arguments[node.argumentList.arguments.indexOf(childrenArg) + 1]; + builder.addDeletion(SourceRange(childrenArg.offset, nextArg.offset - childrenArg.offset)); + }); + } + } + } +} diff --git a/packages/jaspr_lints/lib/src/utils.dart b/packages/jaspr_lints/lib/src/utils.dart index 0a2ac94f..5c248b81 100644 --- a/packages/jaspr_lints/lib/src/utils.dart +++ b/packages/jaspr_lints/lib/src/utils.dart @@ -13,6 +13,12 @@ bool isComponentType(DartType? type) { return type != null && type.accept(IsComponentVisitor()); } +bool isComponentListType(DartType? type) { + return type != null && + type.isDartCoreList && + (type as InterfaceType).typeArguments.first.accept(IsComponentVisitor()); +} + int getLineIndent(LineInfo lineInfo, AstNode node) { var lineNumber = lineInfo.getLocation(node.offset).lineNumber - 1; var lineOffset = lineInfo.getOffsetOfLine(lineNumber); diff --git a/packages/jaspr_lints/pubspec.yaml b/packages/jaspr_lints/pubspec.yaml index ef9b610d..3959968c 100644 --- a/packages/jaspr_lints/pubspec.yaml +++ b/packages/jaspr_lints/pubspec.yaml @@ -19,6 +19,8 @@ dependencies: analyzer: ^6.5.0 analyzer_plugin: ^0.11.0 custom_lint_builder: ^0.6.5 + path: ^1.9.0 + yaml: ^3.1.2 dev_dependencies: lints: ^3.0.0 From 2d03f84482d0d8ebb3121e0b2b24da3d2f088266 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 19:51:58 +0200 Subject: [PATCH 10/18] code cleanup --- apps/fluttercon/lib/app.dart | 43 ++++++++++--------- .../lib/components/session_card.dart | 2 - 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/fluttercon/lib/app.dart b/apps/fluttercon/lib/app.dart index c4025a03..45497952 100644 --- a/apps/fluttercon/lib/app.dart +++ b/apps/fluttercon/lib/app.dart @@ -21,27 +21,28 @@ class App extends AsyncStatelessComponent { final sessions = (sessionsJson as List).map((s) => SessionMapper.fromMap(s)).toList(); yield Router( - redirect: (context, state) { - if (state.location == '/') return '/day-1'; - return null; - }, - routes: [ - for (var i = 1; i < 4; i++) - Route( - path: '/day-$i', - title: 'Fluttercon Berlin 2024', - builder: (context, state) => SchedulePage( - day: i, - sessions: sessions.where((s) => s.startsAt.day == i + 2).toList(), - ), + redirect: (context, state) { + if (state.location == '/') return '/day-1'; + return null; + }, + routes: [ + for (var i = 1; i < 4; i++) + Route( + path: '/day-$i', + title: 'Fluttercon Berlin 2024', + builder: (context, state) => SchedulePage( + day: i, + sessions: sessions.where((s) => s.startsAt.day == i + 2).toList(), ), - Route(path: '/favorites', title: 'Favorites', builder: (context, state) => FavoritesPage()), - for (var session in sessions) - Route( - path: '/${session.slug}', - title: session.title, - builder: (context, state) => SessionPage(session: session), - ), - ]); + ), + Route(path: '/favorites', title: 'Favorites', builder: (context, state) => FavoritesPage()), + for (var session in sessions) + Route( + path: '/${session.slug}', + title: session.title, + builder: (context, state) => SessionPage(session: session), + ), + ], + ); } } diff --git a/apps/fluttercon/lib/components/session_card.dart b/apps/fluttercon/lib/components/session_card.dart index cd52dbd0..f4ee6be6 100644 --- a/apps/fluttercon/lib/components/session_card.dart +++ b/apps/fluttercon/lib/components/session_card.dart @@ -42,7 +42,5 @@ class SessionCard extends StatelessComponent { css('div').flexbox(), css('button').box(position: Position.absolute(right: 10.px, top: 10.px)), ]), - ...LikeButton.styles, - ...Tag.styles, ]; } From bd90678d74e8b81054b7233e3e2fa16a51f9fdad Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 21:59:23 +0200 Subject: [PATCH 11/18] add prefer_html_method lint --- packages/jaspr/tool/generate_html.dart | 6 + packages/jaspr_lints/lib/jaspr_lints.dart | 2 + .../jaspr_lints/lib/src/all_html_tags.dart | 70 +++++++++++ .../lib/src/assists/tree_assist.dart | 20 ++- .../src/lints/prefer_html_method_lint.dart | 116 ++++++++++++++++++ .../sort_children_properties_last_lint.dart | 37 +++--- 6 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 packages/jaspr_lints/lib/src/all_html_tags.dart create mode 100644 packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart diff --git a/packages/jaspr/tool/generate_html.dart b/packages/jaspr/tool/generate_html.dart index 731b0a91..b4c41533 100644 --- a/packages/jaspr/tool/generate_html.dart +++ b/packages/jaspr/tool/generate_html.dart @@ -5,6 +5,8 @@ void main() { var specFile = File('tool/data/html.json'); var specJson = jsonDecode(specFile.readAsStringSync()) as Map; + var allTags = {}; + for (var key in specJson.keys) { var group = specJson[key] as Map; var file = File('lib/src/components/html/$key.dart'); @@ -19,6 +21,7 @@ void main() { continue; } + allTags.add(tag); content.write('\n${data['doc'].split('\n').map((t) => '/// $t\n').join()}'); var attrs = data['attributes'] as Map?; @@ -206,4 +209,7 @@ void main() { file.writeAsStringSync(content.toString()); } + + var lintFile = File('../jaspr_lints/lib/src/all_html_tags.dart'); + lintFile.writeAsStringSync('const allHtmlTags = {${allTags.map((t) => "'$t'").join(', ')}};\n'); } diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index 8b011fc0..8fa244e0 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,6 +1,7 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:jaspr_lints/src/assists/css_assist.dart'; import 'package:jaspr_lints/src/assists/import_assist.dart'; +import 'package:jaspr_lints/src/lints/prefer_html_method_lint.dart'; import 'package:jaspr_lints/src/lints/sort_children_properties_last_lint.dart'; import 'src/assists/component_assist.dart'; @@ -13,6 +14,7 @@ class JasprLinter extends PluginBase { @override List getLintRules(CustomLintConfigs configs) => [ SortChildrenPropertiesLastLint(), + PreferHtmlMethodLint(), ]; @override diff --git a/packages/jaspr_lints/lib/src/all_html_tags.dart b/packages/jaspr_lints/lib/src/all_html_tags.dart new file mode 100644 index 00000000..33397a69 --- /dev/null +++ b/packages/jaspr_lints/lib/src/all_html_tags.dart @@ -0,0 +1,70 @@ +const allHtmlTags = { + 'article', + 'aside', + 'body', + 'footer', + 'head', + 'header', + 'html', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'main_', + 'nav', + 'section', + 'blockquote', + 'div', + 'ul', + 'ol', + 'li', + 'hr', + 'p', + 'pre', + 'a', + 'b', + 'br', + 'code', + 'em', + 'i', + 's', + 'small', + 'span', + 'strong', + 'u', + 'audio', + 'img', + 'video', + 'embed', + 'iframe', + 'object', + 'source', + 'button', + 'form', + 'input', + 'label', + 'datalist', + 'legend', + 'meter', + 'progress', + 'optgroup', + 'option', + 'select', + 'fieldset', + 'textarea', + 'details', + 'dialog', + 'summary', + 'link', + 'script', + 'svg', + 'rect', + 'circle', + 'ellipse', + 'line', + 'path', + 'polygon', + 'polyline' +}; diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index 3fecfcc6..1d192eb8 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -43,7 +43,7 @@ class TreeAssistProvider extends DartAssist { var lines = content.substring(node.offset, node.end).split('\n'); var htmlSource = - '([\n${''.padLeft(lineIndent)}${lines.map((s) => ' ' + s).join('\n')},\n${''.padLeft(lineIndent)}])'; + '([\n${''.padLeft(lineIndent)}${lines.map((s) => ' $s').join('\n')},\n${''.padLeft(lineIndent)}])'; void wrapWith(String name, [List? suggestions]) { final cb = reporter.createChangeBuilder( @@ -82,10 +82,26 @@ class TreeAssistProvider extends DartAssist { edit.write(''.padLeft(lineIndent, ' ')); edit.write(' child: '); edit.writeln(lines[0]); - edit.write(lines.skip(1).map((s) => ' ' + s).join('\n')); + edit.write(lines.skip(1).map((s) => ' $s').join('\n')); edit.write(',\n${''.padLeft(lineIndent, ' ')})'); }); }); + + final cb2 = reporter.createChangeBuilder( + priority: 2, + message: 'Wrap with Builder', + ); + + cb2.addDartFileEdit((builder) { + builder.addReplacement(node.sourceRange, (edit) { + edit.write('Builder(builder: (context) sync* {\n'); + edit.write(''.padLeft(lineIndent, ' ')); + edit.write(' yield '); + edit.write(lines[0]); + edit.write(lines.skip(1).map((s) => '\n $s').join()); + edit.write(';\n${''.padLeft(lineIndent, ' ')}})'); + }); + }); } void removeComponent(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent, diff --git a/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart b/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart new file mode 100644 index 00000000..4cfda8f9 --- /dev/null +++ b/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart @@ -0,0 +1,116 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/error/error.dart' show AnalysisError; +import 'package:analyzer/error/listener.dart'; +import 'package:analyzer/source/source_range.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +import '../all_html_tags.dart'; +import '../utils.dart'; + +class PreferHtmlMethodLint extends DartLintRule { + PreferHtmlMethodLint() + : super( + code: LintCode( + name: 'prefer_html_method', + problemMessage: 'Prefer a html component method over DomComponent', + correctionMessage: "Replace 'DomComponent(tag: \"{0}\", ...)' with '{0}(...)'", + ), + ); + + @override + void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) { + context.registry.addInstanceCreationExpression((node) { + if (node.constructorName.type.name2.lexeme != 'DomComponent') { + return; + } + if (!isComponentType(node.staticType)) { + return; + } + var tag = node.argumentList.arguments + .whereType() + .where((n) => n.name.label.name == 'tag') + .map((n) => n.expression) + .whereType() + .firstOrNull + ?.value; + if (tag == null || !allHtmlTags.contains(tag)) { + return; + } + reporter.atOffset( + offset: node.offset, + length: node.length, + errorCode: code, + arguments: [tag], + data: (node, tag), + ); + }); + } + + @override + List getFixes() => [PreferHtmlMethodFix()]; +} + +class PreferHtmlMethodFix extends DartFix { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + if (analysisError.data case (InstanceCreationExpression node, String tag)) { + reporter.createChangeBuilder(message: 'Convert to html method', priority: 2).addDartFileEdit((builder) { + var content = resolver.source.contents.data; + + Expression? childrenArg; + Expression? childArg; + + for (var argument in node.argumentList.arguments) { + if (argument is NamedExpression) { + var name = argument.name.label.name; + if (name == 'tag') { + var end = argument.endToken.next?.lexeme == ',' ? argument.endToken.next!.end : argument.end; + builder.addDeletion(SourceRange(argument.offset, end - argument.offset)); + } else if (name == 'children') { + builder.addDeletion(argument.name.sourceRange); + childrenArg = argument.expression; + } else if (name == 'child') { + var end = argument.endToken.next?.lexeme == ',' ? argument.endToken.next!.end : argument.end; + builder.addDeletion(SourceRange(argument.offset, end - argument.offset)); + childArg = argument.expression; + } + } + } + + builder.addReplacement(node.constructorName.sourceRange, (edit) { + edit.write(tag); + }); + + if (childArg != null) { + var end = childArg.endToken.next?.lexeme == ',' ? childArg.endToken.next!.end : childArg.end; + var childSource = content.substring(childArg.offset, end); + if (childrenArg != null) { + if (childrenArg is ListLiteral) { + builder.addInsertion(childrenArg.rightBracket.offset, (edit) { + var hasEndingComma = (childrenArg as ListLiteral).rightBracket.previous?.lexeme == ','; + if (!hasEndingComma) { + edit.write(','); + } + edit.write(childSource); + }); + } + } else { + builder.addInsertion(node.argumentList.rightParenthesis.offset, (edit) { + edit.write('['); + edit.write(childSource); + edit.write(']'); + }); + } + } + + builder.format(node.sourceRange); + }); + } + } +} diff --git a/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart b/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart index 40f46e48..d8c91b4e 100644 --- a/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart +++ b/packages/jaspr_lints/lib/src/lints/sort_children_properties_last_lint.dart @@ -43,25 +43,28 @@ class SortChildrenPropertiesLastLint extends DartLintRule { class SortChildrenPropertiesLastFix extends DartFix { @override - void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, AnalysisError analysisError, - List others) { - if (analysisError.errorCode.name == 'sort_children_properties_last') { - if (analysisError.data case (InvocationExpression node, ListLiteral childrenArg)) { - reporter.createChangeBuilder(message: 'Sort children last', priority: 2).addDartFileEdit((builder) { - var content = resolver.source.contents.data; - var lines = content.substring(childrenArg.offset, childrenArg.end); + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + if (analysisError.data case (InvocationExpression node, ListLiteral childrenArg)) { + reporter.createChangeBuilder(message: 'Sort children last', priority: 2).addDartFileEdit((builder) { + var content = resolver.source.contents.data; + var lines = content.substring(childrenArg.offset, childrenArg.end); - builder.addInsertion(node.argumentList.rightParenthesis.offset, (edit) { - if (node.argumentList.rightParenthesis.previous?.lexeme != ',') { - edit.write(', '); - } - edit.write(lines); - }); - - var nextArg = node.argumentList.arguments[node.argumentList.arguments.indexOf(childrenArg) + 1]; - builder.addDeletion(SourceRange(childrenArg.offset, nextArg.offset - childrenArg.offset)); + builder.addInsertion(node.argumentList.rightParenthesis.offset, (edit) { + if (node.argumentList.rightParenthesis.previous?.lexeme != ',') { + edit.write(', '); + } + edit.write(lines); }); - } + + var nextArg = node.argumentList.arguments[node.argumentList.arguments.indexOf(childrenArg) + 1]; + builder.addDeletion(SourceRange(childrenArg.offset, nextArg.offset - childrenArg.offset)); + }); } } } From 48c54592648a19feaa36724d2e24db83afed632d Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 23:21:48 +0200 Subject: [PATCH 12/18] jaspr_lints release preparations --- .../jaspr_cli/lib/src/command_runner.dart | 2 + .../lib/src/commands/analyze_command.dart | 42 ++++++++++++++++ .../lib/src/commands/create_command.dart | 5 +- .../lib/src/commands/update_command.dart | 2 +- packages/jaspr_cli/lib/src/logging.dart | 10 ++-- .../scaffold/__brick__/analysis_options.yaml | 22 +++++++-- .../tool/scaffold/__brick__/pubspec.yaml | 3 +- packages/jaspr_cli/tool/scaffold/brick.yaml | 11 ----- packages/jaspr_lints/.gitignore | 3 ++ packages/jaspr_lints/LICENSE | 1 + packages/jaspr_lints/README.md | 46 ++++++++++++++---- packages/jaspr_lints/analysis_options.yaml | 33 ++----------- packages/jaspr_lints/lib/jaspr_lints.dart | 10 ++-- .../lib/src/assists/component_assist.dart | 4 +- .../lib/src/assists/import_assist.dart | 1 + .../lib/src/assists/tailwind_assist.dart | 30 ------------ .../lib/src/assists/tree_assist.dart | 3 +- .../src/lints/prefer_html_method_lint.dart | 5 +- packages/jaspr_lints/screenshots/1.png | Bin 91471 -> 0 bytes .../tools/analyzer_plugin/bin/plugin.dart | 7 --- .../tools/analyzer_plugin/pubspec.yaml | 10 ---- 21 files changed, 130 insertions(+), 120 deletions(-) create mode 100644 packages/jaspr_cli/lib/src/commands/analyze_command.dart create mode 120000 packages/jaspr_lints/LICENSE delete mode 100644 packages/jaspr_lints/lib/src/assists/tailwind_assist.dart delete mode 100644 packages/jaspr_lints/screenshots/1.png delete mode 100644 packages/jaspr_lints/tools/analyzer_plugin/bin/plugin.dart delete mode 100644 packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml diff --git a/packages/jaspr_cli/lib/src/command_runner.dart b/packages/jaspr_cli/lib/src/command_runner.dart index b62159f6..6a863ab3 100644 --- a/packages/jaspr_cli/lib/src/command_runner.dart +++ b/packages/jaspr_cli/lib/src/command_runner.dart @@ -7,6 +7,7 @@ import 'package:cli_completion/cli_completion.dart'; import 'package:mason/mason.dart'; import 'package:pub_updater/pub_updater.dart'; +import 'commands/analyze_command.dart'; import 'commands/base_command.dart'; import 'commands/build_command.dart'; import 'commands/clean_command.dart'; @@ -37,6 +38,7 @@ class JasprCommandRunner extends CompletionCommandRunner { addCommand(CreateCommand()); addCommand(ServeCommand()); addCommand(BuildCommand()); + addCommand(AnalyzeCommand()); addCommand(CleanCommand()); addCommand(UpdateCommand()); addCommand(DoctorCommand()); diff --git a/packages/jaspr_cli/lib/src/commands/analyze_command.dart b/packages/jaspr_cli/lib/src/commands/analyze_command.dart new file mode 100644 index 00000000..24da3f09 --- /dev/null +++ b/packages/jaspr_cli/lib/src/commands/analyze_command.dart @@ -0,0 +1,42 @@ +import 'dart:io'; + +import '../logging.dart'; +import 'base_command.dart'; + +class AnalyzeCommand extends BaseCommand { + AnalyzeCommand({super.logger}) { + argParser.addFlag( + 'fatal-infos', + help: 'Treat info level issues as fatal', + defaultsTo: true, + ); + argParser.addFlag( + 'fatal-warnings', + help: 'Treat warning level issues as fatal', + defaultsTo: true, + ); + argParser.addFlag( + 'fix', + help: 'Apply all possible fixes to the lint issues found.', + negatable: false, + ); + } + + @override + String get description => 'Report Jaspr specific lint warnings.'; + + @override + String get name => 'analyze'; + + @override + String get category => 'Tooling'; + + @override + Future run() async { + await super.run(); + + var process = await Process.start('dart', ['run', 'custom_lint', ...?argResults?.arguments]); + + return CommandResult.running(watchProcess('custom_lint', process, tag: Tag.none), stop); + } +} diff --git a/packages/jaspr_cli/lib/src/commands/create_command.dart b/packages/jaspr_cli/lib/src/commands/create_command.dart index 51d50e0c..50cdce33 100644 --- a/packages/jaspr_cli/lib/src/commands/create_command.dart +++ b/packages/jaspr_cli/lib/src/commands/create_command.dart @@ -82,7 +82,7 @@ class CreateCommand extends BaseCommand { } @override - String get description => 'Create a new jaspr project.'; + String get description => 'Create a new Jaspr project.'; @override String get name => 'create'; @@ -123,10 +123,12 @@ class CreateCommand extends BaseCommand { var [ jasprFlutterEmbedVersion, jasprRouterVersion, + jasprLintsVersion, webCompilersVersion, ] = await Future.wait([ updater.getLatestVersion('jaspr_flutter_embed'), updater.getLatestVersion('jaspr_router'), + updater.getLatestVersion('jaspr_lints'), updater.getLatestVersion(webCompilersPackage), ]); @@ -149,6 +151,7 @@ class CreateCommand extends BaseCommand { 'jasprBuilderVersion': jasprBuilderVersion, 'jasprFlutterEmbedVersion': jasprFlutterEmbedVersion, 'jasprRouterVersion': jasprRouterVersion, + 'jasprLintsVersion': jasprLintsVersion, 'webCompilersPackage': webCompilersPackage, 'webCompilersVersion': webCompilersVersion, }, diff --git a/packages/jaspr_cli/lib/src/commands/update_command.dart b/packages/jaspr_cli/lib/src/commands/update_command.dart index bafdf55f..40914ead 100644 --- a/packages/jaspr_cli/lib/src/commands/update_command.dart +++ b/packages/jaspr_cli/lib/src/commands/update_command.dart @@ -14,7 +14,7 @@ class UpdateCommand extends BaseCommand { final PubUpdater _updater = PubUpdater(); @override - final String description = 'Update the jaspr cli.'; + final String description = 'Update the Jaspr cli.'; @override final String name = 'update'; diff --git a/packages/jaspr_cli/lib/src/logging.dart b/packages/jaspr_cli/lib/src/logging.dart index 44672da3..b6fecb69 100644 --- a/packages/jaspr_cli/lib/src/logging.dart +++ b/packages/jaspr_cli/lib/src/logging.dart @@ -13,14 +13,16 @@ enum Tag { cli('CLI', cyan), builder('BUILDER', magenta), server('SERVER', yellow), - flutter('FLUTTER', blue); + flutter('FLUTTER', blue), + none('', black); const Tag(this.name, this.color); final String name; final AnsiCode color; String format() { - return color.wrap('[$name]')!; + if (this == none) return ''; + return color.wrap('[$name] ')!; } } @@ -68,7 +70,7 @@ class Logger { } } - void write(String message, {Tag tag = Tag.cli, Level level = Level.info, ProgressState? progress}) { + void write(String message, {Tag tag = Tag.none, Level level = Level.info, ProgressState? progress}) { if (level == Level.verbose && !verbose) { return; } @@ -84,7 +86,7 @@ class Logger { var showAsProgress = !verbose && progress != null && (progress == ProgressState.running || _progress != null); - String log = '${tag.format()} ${level.format(message.trim())}'; + String log = '${tag.format()}${level.format(message.trim())}'; if (showAsProgress) { _progress ??= logger.progress(log); diff --git a/packages/jaspr_cli/tool/scaffold/__brick__/analysis_options.yaml b/packages/jaspr_cli/tool/scaffold/__brick__/analysis_options.yaml index 119f42ca..795c7eba 100644 --- a/packages/jaspr_cli/tool/scaffold/__brick__/analysis_options.yaml +++ b/packages/jaspr_cli/tool/scaffold/__brick__/analysis_options.yaml @@ -14,15 +14,27 @@ include: package:flutter_lints/flutter.yaml{{/flutter}}{{^flutter}} include: package:lints/recommended.yaml{{/flutter}} -# Uncomment the following section to specify additional rules. +analyzer: + # Jaspr has a custom lint package 'jaspr_lints', which needs the 'custom_lint' analyzer plugin. + # + # Unfortunately, running 'dart analyze' does not pick up the custom lints. Instead, you need to + # run a separate command for this: `jaspr analyze` + plugins: + - custom_lint +# exclude: +# - path/to/excluded/files/** + +# Uncomment the following section to enable or disable additional rules. # linter: # rules: -# - camel_case_types +# camel_case_types: true -# analyzer: -# exclude: -# - path/to/excluded/files/** +# For controlling Jaspr specific lint rules, we need a slightly different config. + +# custom_lint: +# rules: +# prefer_html_methods: false # For more information about the core and recommended set of lints, see # https://dart.dev/go/core-lints diff --git a/packages/jaspr_cli/tool/scaffold/__brick__/pubspec.yaml b/packages/jaspr_cli/tool/scaffold/__brick__/pubspec.yaml index 7a944152..0d4ffa4a 100644 --- a/packages/jaspr_cli/tool/scaffold/__brick__/pubspec.yaml +++ b/packages/jaspr_cli/tool/scaffold/__brick__/pubspec.yaml @@ -20,7 +20,8 @@ dev_dependencies: jaspr_builder: ^{{jasprBuilderVersion}}{{#flutter}} flutter_test: sdk: flutter - flutter_lints: ^3.0.0{{/flutter}}{{^flutter}} + flutter_lints: ^3.0.0{{/flutter}} + jaspr_lints: ^{{jasprLintsVersion}}{{^flutter}} lints: ^3.0.0{{/flutter}} jaspr: diff --git a/packages/jaspr_cli/tool/scaffold/brick.yaml b/packages/jaspr_cli/tool/scaffold/brick.yaml index 1755c75a..b4659cdf 100644 --- a/packages/jaspr_cli/tool/scaffold/brick.yaml +++ b/packages/jaspr_cli/tool/scaffold/brick.yaml @@ -5,14 +5,3 @@ version: 0.1.0 environment: mason: ">=0.1.0-dev.52 <0.1.0" - -vars: - name: - type: string - description: - type: string - mode: - type: string - flutter: - type: boolean - diff --git a/packages/jaspr_lints/.gitignore b/packages/jaspr_lints/.gitignore index 3cceda55..58d8eee5 100644 --- a/packages/jaspr_lints/.gitignore +++ b/packages/jaspr_lints/.gitignore @@ -5,3 +5,6 @@ # Avoid committing pubspec.lock for library packages; see # https://dart.dev/guides/libraries/private-files#pubspeclock. pubspec.lock + +.test_cov.dart +coverage/ diff --git a/packages/jaspr_lints/LICENSE b/packages/jaspr_lints/LICENSE new file mode 120000 index 00000000..30cff740 --- /dev/null +++ b/packages/jaspr_lints/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/jaspr_lints/README.md b/packages/jaspr_lints/README.md index b45ef64c..2cdeeb3d 100644 --- a/packages/jaspr_lints/README.md +++ b/packages/jaspr_lints/README.md @@ -1,14 +1,26 @@ # Jaspr Lints -This package provides lints and code assists for jaspr projects. +This package provides lints and code assists for Jaspr projects. -### Features: -* Create a new StatelessComponent / StatefulComponent / InheritedComponent -* Remove components from the component tree -* Add components tp the component tree -* More to come +## Code Assists: -### Setup: +- Create `StatelessComponent` / `StatefulComponent` / `InheritedComponent` +- Convert `StatelessComponent` to `StatefulComponent` +- Convert `StatelessComponent` to `AsyncStatelessComponent` +- Remove component from the tree +- Wrap component with `div()` / `section()` / `ul()` or any html component +- Wrap component with other component +- Wrap component with `Builder` +- Extract subtree into `StatelessComponent` +- Add styles to html component +- Convert import to web-only / server-only import + +## Lints + +- Prefer html methods `div(...)` over `DomComponent(tag: 'div', ...)`. **(Fix available)** +- Sort children properties last in html component methods. **(Fix available)** + +## Setup: * Add `jaspr_lints` as dev dependency: ```shell @@ -19,9 +31,23 @@ dart pub add jaspr_lints --dev ```yaml analyzer: plugins: - - jaspr_lints + - custom_lints ``` -After running `pub get` you now see additional Jaspr lints when invoking code assist on a component function like `div()` or `p()` files: +After running `pub get` you now see additional Jaspr lints +when invoking code assist on a component function like `div()` or `p()` files. + +## Usage -![Example screenshot of what Jaspr code assists look like](screenshots/1.png "Jaspr Code Assists") +Unfortunately, running `dart analyze` does not pick up the custom lints. +Instead, you need to run a separate command for this: `jaspr analyze` + +Also enabling / disabling specific lint rules works slightly different. +In `analysis_options.yaml` add the following section: + +```yaml +custom_lint: + rules: + prefer_html_methods: true + sort_children_properties_last: true +``` diff --git a/packages/jaspr_lints/analysis_options.yaml b/packages/jaspr_lints/analysis_options.yaml index dee8927a..ce58417e 100644 --- a/packages/jaspr_lints/analysis_options.yaml +++ b/packages/jaspr_lints/analysis_options.yaml @@ -1,30 +1,7 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - include: package:lints/recommended.yaml -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -# analyzer: -# exclude: -# - path/to/excluded/files/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options +linter: + rules: + sort_pub_dependencies: true + prefer_relative_imports: true + directives_ordering: true diff --git a/packages/jaspr_lints/lib/jaspr_lints.dart b/packages/jaspr_lints/lib/jaspr_lints.dart index 8fa244e0..106857dc 100644 --- a/packages/jaspr_lints/lib/jaspr_lints.dart +++ b/packages/jaspr_lints/lib/jaspr_lints.dart @@ -1,12 +1,11 @@ import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:jaspr_lints/src/assists/css_assist.dart'; -import 'package:jaspr_lints/src/assists/import_assist.dart'; -import 'package:jaspr_lints/src/lints/prefer_html_method_lint.dart'; -import 'package:jaspr_lints/src/lints/sort_children_properties_last_lint.dart'; import 'src/assists/component_assist.dart'; -import 'src/assists/tailwind_assist.dart'; +import 'src/assists/css_assist.dart'; +import 'src/assists/import_assist.dart'; import 'src/assists/tree_assist.dart'; +import 'src/lints/prefer_html_method_lint.dart'; +import 'src/lints/sort_children_properties_last_lint.dart'; PluginBase createPlugin() => JasprLinter(); @@ -21,7 +20,6 @@ class JasprLinter extends PluginBase { List getAssists() => [ ComponentAssistProvider(), TreeAssistProvider(), - TailwindAssistProvider(), CssAssistProvider(), ImportAssistProvider(), ]; diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index f742483d..3708a74c 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -135,7 +135,7 @@ class ComponentAssistProvider extends DartAssist { var name = node.name.lexeme; builder.addInsertion(splitToken.offset, (edit) { - edit.write("${indent}@override\n State createState() => ${name}State();\n" + edit.write("$indent@override\n State createState() => ${name}State();\n" "}\n\nclass ${name}State extends State<$name> {\n$endIndent"); }); @@ -180,7 +180,7 @@ class StateBuildVisitor extends UnifyingAstVisitor { var elem = node.staticElement; if (elem == null) return; - if (elem.enclosingElement == clazz.declaredElement) { + if (elem.enclosingElement3 == clazz.declaredElement) { builder.addSimpleInsertion(node.offset, 'component.'); } } diff --git a/packages/jaspr_lints/lib/src/assists/import_assist.dart b/packages/jaspr_lints/lib/src/assists/import_assist.dart index 9550868b..0ac4063e 100644 --- a/packages/jaspr_lints/lib/src/assists/import_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/import_assist.dart @@ -2,6 +2,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/source/source_range.dart'; +// ignore: implementation_imports import 'package:analyzer/src/error/imports_verifier.dart'; import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; diff --git a/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart b/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart deleted file mode 100644 index 4d604727..00000000 --- a/packages/jaspr_lints/lib/src/assists/tailwind_assist.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:analyzer/source/source_range.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; - -class TailwindAssistProvider extends DartAssist { - @override - void run( - CustomLintResolver resolver, - ChangeReporter reporter, - CustomLintContext context, - SourceRange target, - ) { - context.registry.addMethodInvocation((node) { - final method = node.methodName.toString(); - - if (target.length != 0) return; - if (target.offset < node.offset) return; - if (target.offset > node.offset + method.length) return; - - if (method == 'tw') { - // Do Tailwind formatting - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Sort Tailwind classes', - ); - - cb.addDartFileEdit((builder) {}); - } - }); - } -} diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index 1d192eb8..18ebc3f9 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -2,7 +2,8 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/source/source_range.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:jaspr_lints/src/utils.dart'; + +import '../utils.dart'; class TreeAssistProvider extends DartAssist { @override diff --git a/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart b/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart index 4cfda8f9..20a7cf12 100644 --- a/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart +++ b/packages/jaspr_lints/lib/src/lints/prefer_html_method_lint.dart @@ -11,9 +11,8 @@ class PreferHtmlMethodLint extends DartLintRule { PreferHtmlMethodLint() : super( code: LintCode( - name: 'prefer_html_method', - problemMessage: 'Prefer a html component method over DomComponent', - correctionMessage: "Replace 'DomComponent(tag: \"{0}\", ...)' with '{0}(...)'", + name: 'prefer_html_methods', + problemMessage: "Prefer using '{0}(...)' over 'DomComponent(tag: \"{0}\", ...)'", ), ); diff --git a/packages/jaspr_lints/screenshots/1.png b/packages/jaspr_lints/screenshots/1.png deleted file mode 100644 index cfdf960c2a61aabbde885c2477f0f93e07d966f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91471 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 main(List args, SendPort sendPort) async { - await start(args, sendPort); -} diff --git a/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml b/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml deleted file mode 100644 index 70a03d39..00000000 --- a/packages/jaspr_lints/tools/analyzer_plugin/pubspec.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: jaspr_lints_analyzer_plugin_loader -description: This pubspec determines the version of the analyzer plugin to load. -version: 0.1.0 -publish_to: none - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - custom_lint: 0.6.5 From 4577f99ee13552047b5197170b3e9ed86a2a4bef Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 23:25:08 +0200 Subject: [PATCH 13/18] update changelog --- packages/jaspr/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/jaspr/CHANGELOG.md b/packages/jaspr/CHANGELOG.md index 8bc942e8..d51fed00 100644 --- a/packages/jaspr/CHANGELOG.md +++ b/packages/jaspr/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased minor + +- Include and setup `jaspr_lints` in newly created projects. +- Added `jaspr analyze` command to check all custom lints. + ## 0.15.0 - Added support for using `@css` and `@encoder`/`@decoder` across other packages. From 19c188e6a95e050f298f074085c2ae7633c68490 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 23:41:31 +0200 Subject: [PATCH 14/18] add linting docs --- docs.json | 3 +- docs/get_started/linting.mdx | 70 ++++++++++++++++++++++++++++++++++ packages/jaspr_lints/README.md | 2 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 docs/get_started/linting.mdx diff --git a/docs.json b/docs.json index 29effd04..e3019872 100644 --- a/docs.json +++ b/docs.json @@ -16,7 +16,8 @@ ["\uD83D\uDD79 Jaspr CLI", "/get_started/cli"], ["\uD83D\uDCDF Rendering Modes", "/get_started/modes"], ["\uD83D\uDCA7 Hydration", "/get_started/hydration"], - ["\uD83D\uDCE6 Project Structure", "/get_started/project_structure"] + ["\uD83D\uDCE6 Project Structure", "/get_started/project_structure"], + ["\uD83E\uDDF9 Linting", "/get_started/linting"] ]], ["Key Concepts", [ ["\uD83E\uDDF1 Component System", "/concepts/components"], diff --git a/docs/get_started/linting.mdx b/docs/get_started/linting.mdx new file mode 100644 index 00000000..49ae9c17 --- /dev/null +++ b/docs/get_started/linting.mdx @@ -0,0 +1,70 @@ +--- +title: Linting +description: Jaspr comes with its own set of lint rules and code assists. +--- + +Jaspr has its own linting and analyzer package called `jaspr_lints`. This comes pre-configured +with every new project and enables the following set of lint rules and code assists: + +## Lint Rules + + + + Prefer html methods like `div(...)` over `DomComponent(tag: 'div', ...)`. + + **BAD:** + ```dart + yield DomComponent( + tag: 'div', + children: [ + DomComponent( + tag: 'p', + child: Text('Hello World'), + ), + ], + ); + ``` + + **GOOD:** + yield div([ + p([text('Hello World')]), + ]); + ``` + + + + + + Sort children properties last in html component methods. + + This improves readability and best represents actual html. + + **BAD:** + ```dart + yield div([ + p([text('Hello World')], classes: 'text-red'), + ], id: 'main'); + ``` + + **GOOD:** + yield div(id: 'main', [ + p(classes: 'text-red', [text('Hello World')]), + ]); + ``` + + + +## Code Assists + + + **Create `StatelessComponent` / `StatefulComponent` / `InheritedComponent`** + + + + **Convert `StatelessComponent` to `StatefulComponent`** + + + + **Convert `StatelessComponent` to `AsyncStatelessComponent`** + + diff --git a/packages/jaspr_lints/README.md b/packages/jaspr_lints/README.md index 2cdeeb3d..eccfad2c 100644 --- a/packages/jaspr_lints/README.md +++ b/packages/jaspr_lints/README.md @@ -17,7 +17,7 @@ This package provides lints and code assists for Jaspr projects. ## Lints -- Prefer html methods `div(...)` over `DomComponent(tag: 'div', ...)`. **(Fix available)** +- Prefer html methods like `div(...)` over `DomComponent(tag: 'div', ...)`. **(Fix available)** - Sort children properties last in html component methods. **(Fix available)** ## Setup: From 720ad70a4ed619f383844221212f590996d20892 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Wed, 11 Sep 2024 23:49:50 +0200 Subject: [PATCH 15/18] fix docs --- docs/get_started/linting.mdx | 69 ++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/get_started/linting.mdx b/docs/get_started/linting.mdx index 49ae9c17..558c94c7 100644 --- a/docs/get_started/linting.mdx +++ b/docs/get_started/linting.mdx @@ -26,6 +26,7 @@ with every new project and enables the following set of lint rules and code assi ``` **GOOD:** + ```dart yield div([ p([text('Hello World')]), ]); @@ -47,6 +48,7 @@ with every new project and enables the following set of lint rules and code assi ``` **GOOD:** + ```dart yield div(id: 'main', [ p(classes: 'text-red', [text('Hello World')]), ]); @@ -57,14 +59,75 @@ with every new project and enables the following set of lint rules and code assi ## Code Assists - **Create `StatelessComponent` / `StatefulComponent` / `InheritedComponent`** + + ```dart + class [...] extends StatelessComponent { + [...]({super.key}); + + Iterable build(BuildContext context) sync* { + yield [...]; + } + } + ``` + + + + + + + + + + + + + + + + + + + + + - **Convert `StatelessComponent` to `StatefulComponent`** + + - **Convert `StatelessComponent` to `AsyncStatelessComponent`** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d69121b8bcae81ae7f40265651b865da8101a19a Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Thu, 12 Sep 2024 09:30:22 +0200 Subject: [PATCH 16/18] apply code review --- docs/get_started/linting.mdx | 2 +- .../lib/src/assists/component_assist.dart | 223 ++++++++++-------- .../lib/src/assists/tree_assist.dart | 35 +-- packages/jaspr_lints/pubspec.yaml | 4 +- 4 files changed, 129 insertions(+), 135 deletions(-) diff --git a/docs/get_started/linting.mdx b/docs/get_started/linting.mdx index 558c94c7..ea7e6837 100644 --- a/docs/get_started/linting.mdx +++ b/docs/get_started/linting.mdx @@ -83,7 +83,7 @@ with every new project and enables the following set of lint rules and code assi - + diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index 3708a74c..b2699eab 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -35,75 +35,9 @@ class ComponentAssistProvider extends DartAssist { nameSuggestion = nameSuggestion.split('_').map((s) => s.substring(0, 1).toUpperCase() + s.substring(1)).join(); - reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { - builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { - edit.write('class '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write(' extends StatelessComponent {\n' - ' const '); - - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('({super.key});\n\n @override\n Iterable build(BuildContext context) sync* {\n' - ' yield '); - edit.addSimpleLinkedEdit('child', "div([])"); - edit.write(';\n }\n}\n'); - }); - if (!hasJasprImport) { - builder.addSimpleInsertion(0, "import 'package:jaspr/jaspr.dart';\n"); - } - }); - - reporter.createChangeBuilder(priority: 2, message: 'Create StatefulComponent').addDartFileEdit((builder) { - builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { - edit.write('class '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write(' extends StatefulComponent {\n' - ' const '); - - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('({super.key});\n\n @override\n State createState() => '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('State();\n}\n\nclass '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('State extends State<'); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('> {\n\n @override\n Iterable build(BuildContext context) sync* {\n' - ' yield '); - edit.addSimpleLinkedEdit('child', "div([])"); - edit.write(';\n }\n}\n'); - }); - if (!hasJasprImport) { - builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); - } - }); - - reporter.createChangeBuilder(priority: 3, message: 'Create InheritedComponent').addDartFileEdit((builder) { - if (!hasJasprImport) { - builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); - } - - builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { - edit.write('class '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write(' extends InheritedComponent {\n' - ' const '); - - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('({super.child, super.children, super.key});\n\n static '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write(' of(BuildContext context) {\n final '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('? result = context.dependOnInheritedComponentOfExactType<'); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write('>();\n assert(result != null, \'No '); - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write( - ' found in context.\');\n return result!;\n }\n\n @override\n bool updateShouldNotify(covariant '); - - edit.addSimpleLinkedEdit('name', nameSuggestion); - edit.write(' oldComponent) {\n return false;\n }\n}\n'); - }); - }); + createStatelessComponent(reporter, target, nameSuggestion, hasJasprImport); + createStatefulComponent(reporter, target, nameSuggestion, hasJasprImport); + createInheritedComponent(reporter, target, nameSuggestion, hasJasprImport); }); context.registry.addClassDeclaration((node) { @@ -124,46 +58,130 @@ class ComponentAssistProvider extends DartAssist { } } - reporter.createChangeBuilder(priority: 1, message: 'Convert to StatefulComponent').addDartFileEdit((builder) { - builder.addReplacement(node.extendsClause!.superclass.sourceRange, (edit) { - edit.write('StatefulComponent'); - }); + convertToStatefulComponent(reporter, node, buildMethod); + if (buildMethod != null && buildMethod.body.keyword != null) { + convertToAsyncStatelessComponent(reporter, node, buildMethod); + } + }); + } - var splitToken = buildMethod?.beginToken ?? node.rightBracket; - var indent = buildMethod != null ? '' : ' '; - var endIndent = buildMethod != null ? ' ' : ''; - var name = node.name.lexeme; + void createStatelessComponent( + ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends StatelessComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.key});\n\n @override\n Iterable build(BuildContext context) sync* {\n' + ' yield '); + edit.addSimpleLinkedEdit('child', "div([])"); + edit.write(';\n }\n}\n'); + }); + if (!hasJasprImport) { + builder.addSimpleInsertion(0, "import 'package:jaspr/jaspr.dart';\n"); + } + }); + } - builder.addInsertion(splitToken.offset, (edit) { - edit.write("$indent@override\n State createState() => ${name}State();\n" - "}\n\nclass ${name}State extends State<$name> {\n$endIndent"); - }); + void createStatefulComponent( + ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + reporter.createChangeBuilder(priority: 1, message: 'Create StatefulComponent').addDartFileEdit((builder) { + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends StatefulComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.key});\n\n @override\n State createState() => '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('State();\n}\n\nclass '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('State extends State<'); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('> {\n\n @override\n Iterable build(BuildContext context) sync* {\n' + ' yield '); + edit.addSimpleLinkedEdit('child', "div([])"); + edit.write(';\n }\n}\n'); + }); + if (!hasJasprImport) { + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); + } + }); + } - buildMethod?.body.visitChildren(StateBuildVisitor(builder, node)); + void createInheritedComponent( + ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + reporter.createChangeBuilder(priority: 1, message: 'Create InheritedComponent').addDartFileEdit((builder) { + if (!hasJasprImport) { + builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); + } + + builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { + edit.write('class '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' extends InheritedComponent {\n' + ' const '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('({super.child, super.children, super.key});\n\n static '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' of(BuildContext context) {\n final '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('? result = context.dependOnInheritedComponentOfExactType<'); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write('>();\n assert(result != null, \'No '); + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write( + ' found in context.\');\n return result!;\n }\n\n @override\n bool updateShouldNotify(covariant '); + + edit.addSimpleLinkedEdit('name', nameSuggestion); + edit.write(' oldComponent) {\n return false;\n }\n}\n'); }); + }); + } - if (buildMethod != null && buildMethod.body.keyword != null) { - reporter - .createChangeBuilder(priority: 1, message: 'Convert to AsyncStatelessComponent') - .addDartFileEdit((builder) { - if (node.parent case CompilationUnit unit) { - for (var dir in unit.directives) { - if (dir is ImportDirective && dir.uri.stringValue == 'package:jaspr/jaspr.dart') { - builder.addDeletion(dir.sourceRange); - } - } - } + void convertToStatefulComponent(ChangeReporter reporter, ClassDeclaration node, MethodDeclaration? buildMethod) { + reporter.createChangeBuilder(priority: 1, message: 'Convert to StatefulComponent').addDartFileEdit((builder) { + builder.addReplacement(node.extendsClause!.superclass.sourceRange, (edit) { + edit.write('StatefulComponent'); + }); - builder.importLibrary(Uri.parse('package:jaspr/server.dart')); + var splitToken = buildMethod?.beginToken ?? node.rightBracket; + var indent = buildMethod != null ? '' : ' '; + var endIndent = buildMethod != null ? ' ' : ''; + var name = node.name.lexeme; - builder.addSimpleReplacement(node.extendsClause!.superclass.sourceRange, 'AsyncStatelessComponent'); - builder.addSimpleReplacement(buildMethod!.body.keyword!.sourceRange, 'async'); - if (buildMethod.returnType != null) { - builder.addSimpleReplacement(buildMethod.returnType!.sourceRange, 'Stream'); - } else { - builder.addSimpleInsertion(buildMethod.name.offset, 'Stream '); + builder.addInsertion(splitToken.offset, (edit) { + edit.write("$indent@override\n State createState() => ${name}State();\n" + "}\n\nclass ${name}State extends State<$name> {\n$endIndent"); + }); + + buildMethod?.body.visitChildren(StateBuildVisitor(builder, node)); + }); + } + + void convertToAsyncStatelessComponent(ChangeReporter reporter, ClassDeclaration node, MethodDeclaration buildMethod) { + reporter.createChangeBuilder(priority: 1, message: 'Convert to AsyncStatelessComponent').addDartFileEdit((builder) { + if (node.parent case CompilationUnit unit) { + for (var dir in unit.directives) { + if (dir is ImportDirective && dir.uri.stringValue == 'package:jaspr/jaspr.dart') { + builder.addDeletion(dir.sourceRange); } - }); + } + } + + builder.importLibrary(Uri.parse('package:jaspr/server.dart')); + + builder.addSimpleReplacement(node.extendsClause!.superclass.sourceRange, 'AsyncStatelessComponent'); + builder.addSimpleReplacement(buildMethod.body.keyword!.sourceRange, 'async'); + if (buildMethod.returnType != null) { + builder.addSimpleReplacement(buildMethod.returnType!.sourceRange, 'Stream'); + } else { + builder.addSimpleInsertion(buildMethod.name.offset, 'Stream '); } }); } @@ -180,7 +198,8 @@ class StateBuildVisitor extends UnifyingAstVisitor { var elem = node.staticElement; if (elem == null) return; - if (elem.enclosingElement3 == clazz.declaredElement) { + // ignore: deprecated_member_use + if (elem.enclosingElement == clazz.declaredElement) { builder.addSimpleInsertion(node.offset, 'component.'); } } diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index 18ebc3f9..319b7c2c 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -71,12 +71,7 @@ class TreeAssistProvider extends DartAssist { wrapWith('ul'); wrapWith('li'); - final cb = reporter.createChangeBuilder( - priority: 2, - message: 'Wrap with component...', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 2, message: 'Wrap with component...').addDartFileEdit((builder) { builder.addReplacement(node.sourceRange, (edit) { edit.addSimpleLinkedEdit('comp', 'component'); edit.write('(\n'); @@ -88,12 +83,7 @@ class TreeAssistProvider extends DartAssist { }); }); - final cb2 = reporter.createChangeBuilder( - priority: 2, - message: 'Wrap with Builder', - ); - - cb2.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 2, message: 'Wrap with Builder').addDartFileEdit((builder) { builder.addReplacement(node.sourceRange, (edit) { edit.write('Builder(builder: (context) sync* {\n'); edit.write(''.padLeft(lineIndent, ' ')); @@ -127,12 +117,7 @@ class TreeAssistProvider extends DartAssist { var content = resolver.source.contents.data; if (children.length == 1) { - final cb = reporter.createChangeBuilder( - priority: 3, - message: 'Remove this component', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 3, message: 'Remove this component').addDartFileEdit((builder) { builder.addReplacement(node.sourceRange, (edit) { var child = children.first; var childIndent = getLineIndent(resolver.lineInfo, child); @@ -140,12 +125,7 @@ class TreeAssistProvider extends DartAssist { }); }); } else if (node.parent is ListLiteral) { - final cb = reporter.createChangeBuilder( - priority: 3, - message: 'Remove this component', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 3, message: 'Remove this component').addDartFileEdit((builder) { builder.addReplacement(node.sourceRange, (edit) { for (var child in children) { var childIndent = getLineIndent(resolver.lineInfo, child); @@ -162,12 +142,7 @@ class TreeAssistProvider extends DartAssist { }); }); } else if (node.parent is YieldStatement) { - final cb = reporter.createChangeBuilder( - priority: 3, - message: 'Remove this component', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 3, message: 'Remove this component').addDartFileEdit((builder) { builder.addReplacement(node.parent!.sourceRange, (edit) { for (var child in children) { var childIndent = getLineIndent(resolver.lineInfo, child); diff --git a/packages/jaspr_lints/pubspec.yaml b/packages/jaspr_lints/pubspec.yaml index 3959968c..6b3c410e 100644 --- a/packages/jaspr_lints/pubspec.yaml +++ b/packages/jaspr_lints/pubspec.yaml @@ -3,7 +3,7 @@ description: A collection of lints and assists for jaspr projects. version: 0.1.0 homepage: https://github.com/schultek/jaspr issue_tracker: https://github.com/schultek/jaspr/issues -documentation: https://docs.page/schultek/jaspr +documentation: https://docs.page/schultek/jaspr/get_started/linting funding: - https://github.com/sponsors/schultek @@ -23,4 +23,4 @@ dependencies: yaml: ^3.1.2 dev_dependencies: - lints: ^3.0.0 + lints: ^4.0.0 From fc4194648ae8ea02553999bd9fa5d974afb1f544 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Thu, 12 Sep 2024 09:55:48 +0200 Subject: [PATCH 17/18] update docs --- README.md | 3 +- docs/get_started/linting.mdx | 116 ++++++++++++++++++++++++++++------- docs/utils/at_import.mdx | 4 ++ 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 1e1b7b87..a71d9a68 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,11 @@ Rather it embraces these differences to give the best of both worlds. - **/examples**: Well-maintained and documented examples - **/experiments**: Experimental apps or features, that are not part of the core framework (yet?) (may be broken). - **/packages**: - - [**/jaspr**](https://github.com/schultek/jaspr/tree/main/packages/jaspr): The core framework package. + - **/jaspr**: The core framework package. - **/jaspr_builder**: Code-generation builders for jaspr. - **/jaspr_cli**: The command line interface of jaspr. - **/jaspr_flutter_embed**: Flutter element embedding bindings for jaspr. + - **/jaspr_lints**: A collection of lints and assists for jaspr projects. - **/jaspr_riverpod**: Riverpod implementation for jaspr. - **/jaspr_router**: A router implementation for jaspr. - **/jaspr_test**: A testing package for jaspr. diff --git a/docs/get_started/linting.mdx b/docs/get_started/linting.mdx index ea7e6837..0a952e0e 100644 --- a/docs/get_started/linting.mdx +++ b/docs/get_started/linting.mdx @@ -59,75 +59,149 @@ with every new project and enables the following set of lint rules and code assi ## Code Assists - - ```dart - class [...] extends StatelessComponent { - [...]({super.key}); - - Iterable build(BuildContext context) sync* { - yield [...]; - } - } - ``` + + Creates a new `StatelessComponent` class. - + + Creates a new `StatefulComponent` and respective `State` class. - + + Creates a new `InheritedComponent` class. - + + Converts a custom `StatelessComponent` into a `StatefulComponent` and respective `State` class. - + + Converts a custom `StatelessComponent` into an `AsyncStatelessComponent`. - + + Surgically removes the selected component from the component tree, making its + children the new direct children of its parent. - + + Wraps the selected component with a new html component. + + ```dart + yield div([ + p([ // [!code ++] + span([text('Hello World')]), + ]), // [!code ++] + ]); + ``` - + + Wraps the selected component with a new component. + + ```dart + yield div([ + MyComponent( // [!code ++] + child: span([text('Hello World')]), + ), // [!code ++] + ]); + ``` - + + Wraps the selected component with a `Builder`. + + ```dart + yield div([ + Builder(builder: (context) sync* { // [!code ++] + yield span([text('Hello World')]); + }), // [!code ++] + ]); + ``` - + + Extracts the selected component plus subtree into a new `StatelessComponent`. - + + Adds new css styles to the selected component. + + This will either add a new class name: + ```dart + yield div( + classes: '[...]' // [!code ++] + [], + ); + + /* ... */ + + @css // [!code ++] + static List styles = [ // [!code ++] + css('.[...]').box(...), // [!code ++] + ]; // [!code ++] + ``` + + Or use an existing id or class name: + ```dart + yield div(id: 'content', []); + + /* ... */ + + @css // [!code ++] + static List styles = [ // [!code ++] + css('#content').box(...), // [!code ++] + ]; // [!code ++] + ``` - + + Converts any import to a web-only import using the [@Import](/utils/at_import) annotation. + + ```dart + import 'dart:html'; // [!code --] + + @Import.onWeb('dart:html', show: [#window]) // [!code ++] + import 'filename.imports.dart'; // [!code ++] + ``` + + This will auto-detect all used members from the import and add them to the `show` parameter. + Converts any import to a server-only import using the [@Import](/utils/at_import) annotation. + + ```dart + import 'dart:io'; // [!code --] + + @Import.onServer('dart:io', show: [#HttpRequest]) // [!code ++] + import 'filename.imports.dart'; // [!code ++] + ``` + + This will auto-detect all used members from the import and add them to the `show` parameter. diff --git a/docs/utils/at_import.mdx b/docs/utils/at_import.mdx index e5117421..7a6fd881 100644 --- a/docs/utils/at_import.mdx +++ b/docs/utils/at_import.mdx @@ -62,6 +62,10 @@ This will: - setup conditional imports for `dart:html` on the web - write stubs on the server for all imported members. + +There is a code-assist available from `jaspr_lints` that converts your imports automatically! +See [Linting](/get_started/linting). + --- You can extend this to multiple imports and mix web and server imports like this: From 57bf532e10e0a7481b8e23345892e5946a7be816 Mon Sep 17 00:00:00 2001 From: Kilian Schulte Date: Thu, 12 Sep 2024 15:37:48 +0200 Subject: [PATCH 18/18] apply review --- .../lib/src/assists/component_assist.dart | 18 ++++- .../lib/src/assists/css_assist.dart | 72 +++++++++---------- .../lib/src/assists/tree_assist.dart | 52 ++++++-------- 3 files changed, 70 insertions(+), 72 deletions(-) diff --git a/packages/jaspr_lints/lib/src/assists/component_assist.dart b/packages/jaspr_lints/lib/src/assists/component_assist.dart index b2699eab..06574e67 100644 --- a/packages/jaspr_lints/lib/src/assists/component_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/component_assist.dart @@ -66,7 +66,11 @@ class ComponentAssistProvider extends DartAssist { } void createStatelessComponent( - ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + ChangeReporter reporter, + SourceRange target, + String nameSuggestion, + bool hasJasprImport, + ) { reporter.createChangeBuilder(priority: 1, message: 'Create StatelessComponent').addDartFileEdit((builder) { builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); @@ -87,7 +91,11 @@ class ComponentAssistProvider extends DartAssist { } void createStatefulComponent( - ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + ChangeReporter reporter, + SourceRange target, + String nameSuggestion, + bool hasJasprImport, + ) { reporter.createChangeBuilder(priority: 1, message: 'Create StatefulComponent').addDartFileEdit((builder) { builder.addInsertion(target.end == 0 ? 1 : target.end, (edit) { edit.write('class '); @@ -114,7 +122,11 @@ class ComponentAssistProvider extends DartAssist { } void createInheritedComponent( - ChangeReporter reporter, SourceRange target, String nameSuggestion, bool hasJasprImport) { + ChangeReporter reporter, + SourceRange target, + String nameSuggestion, + bool hasJasprImport, + ) { reporter.createChangeBuilder(priority: 1, message: 'Create InheritedComponent').addDartFileEdit((builder) { if (!hasJasprImport) { builder.importLibrary(Uri.parse('package:jaspr/jaspr.dart')); diff --git a/packages/jaspr_lints/lib/src/assists/css_assist.dart b/packages/jaspr_lints/lib/src/assists/css_assist.dart index e787b5d4..fa1499d0 100644 --- a/packages/jaspr_lints/lib/src/assists/css_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/css_assist.dart @@ -2,6 +2,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/source/source_range.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import '../utils.dart'; @@ -78,44 +79,36 @@ class CssAssistProvider extends DartAssist { .whereType() .firstOrNull; - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Add styles', - ); - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 1, message: 'Add styles').addDartFileEdit((builder) { + void writeCssRule(DartEditBuilder edit) { + edit.write(' css(\''); + if (idVal != null) { + edit.write('#$idVal\').'); + } else if (classesVal != null) { + edit.write('.$classesVal\').'); + } else { + edit.write('.'); + edit.addSimpleLinkedEdit('className', 'classname'); + edit.write('\').'); + } + edit.addSimpleLinkedEdit( + 'styles', + 'box', + kind: LinkedEditSuggestionKind.METHOD, + suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list'], + ); + edit.write('(),\n '); + } + if (styles == null) { builder.addInsertion(comp.$2.end, (edit) { - edit.write('\n\n @css\n static final List styles = [\n css(\''); - if (idVal != null) { - edit.write('#$idVal\').'); - } else if (classesVal != null) { - edit.write('.$classesVal\').'); - } else { - edit.write('.'); - edit.addSimpleLinkedEdit('className', 'classname'); - edit.write('\').'); - } - edit.addSimpleLinkedEdit('styles', 'box', - kind: LinkedEditSuggestionKind.METHOD, - suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); - edit.write('(),\n ];'); + edit.write('\n\n @css\n static final List styles = [\n '); + writeCssRule(edit); + edit.write('];'); }); } else { builder.addInsertion(styles.rightBracket.offset, (edit) { - edit.write(' css(\''); - if (idVal != null) { - edit.write('#$idVal\').'); - } else if (classesVal != null) { - edit.write('.$classesVal\').'); - } else { - edit.write('.'); - edit.addSimpleLinkedEdit('className', 'classname'); - edit.write('\').'); - } - edit.addSimpleLinkedEdit('styles', 'box', - kind: LinkedEditSuggestionKind.METHOD, - suggestions: ['box', 'text', 'background', 'flexbox', 'flexItem', 'grid', 'gridItem', 'list']); - edit.write('(),\n '); + writeCssRule(edit); }); } @@ -141,7 +134,11 @@ class CssAssistProvider extends DartAssist { } void convertToNested( - CustomLintResolver resolver, ChangeReporter reporter, FunctionExpressionInvocation node, int lineIndent) { + CustomLintResolver resolver, + ChangeReporter reporter, + FunctionExpressionInvocation node, + int lineIndent, + ) { if (node.argumentList.arguments.length != 1) { return; } @@ -149,12 +146,7 @@ class CssAssistProvider extends DartAssist { var selector = node.argumentList.arguments.first; var chain = getFullChain(node.parent); - final cb = reporter.createChangeBuilder( - priority: 1, - message: 'Convert to nested styles', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 1, message: 'Convert to nested styles').addDartFileEdit((builder) { builder.addInsertion(selector.end, (edit) { edit.write(', [\n${''.padLeft(lineIndent)} css(\'&\''); }); diff --git a/packages/jaspr_lints/lib/src/assists/tree_assist.dart b/packages/jaspr_lints/lib/src/assists/tree_assist.dart index 319b7c2c..2c19e8f2 100644 --- a/packages/jaspr_lints/lib/src/assists/tree_assist.dart +++ b/packages/jaspr_lints/lib/src/assists/tree_assist.dart @@ -13,8 +13,8 @@ class TreeAssistProvider extends DartAssist { CustomLintContext context, SourceRange target, ) { - context.registry.addInvocationExpression((node) { - if (!target.coveredBy(node.function.sourceRange)) { + void treeAssistsFor(Expression node, AstNode anchor, ArgumentList argumentList) { + if (!target.coveredBy(anchor.sourceRange)) { return; } if (!isComponentType(node.staticType)) { @@ -22,20 +22,15 @@ class TreeAssistProvider extends DartAssist { } final indent = getLineIndent(resolver.lineInfo, node); wrapComponent(resolver, reporter, node, indent); - removeComponent(resolver, reporter, node, indent, node.argumentList); + removeComponent(resolver, reporter, node, indent, argumentList); extractComponent(resolver, reporter, node, indent); + } + + context.registry.addInvocationExpression((node) { + treeAssistsFor(node, node.function, node.argumentList); }); context.registry.addInstanceCreationExpression((node) { - if (!target.coveredBy(node.constructorName.sourceRange)) { - return; - } - if (!isComponentType(node.staticType)) { - return; - } - final indent = getLineIndent(resolver.lineInfo, node); - wrapComponent(resolver, reporter, node, indent); - removeComponent(resolver, reporter, node, indent, node.argumentList); - extractComponent(resolver, reporter, node, indent); + treeAssistsFor(node, node.constructorName, node.argumentList); }); } @@ -47,16 +42,15 @@ class TreeAssistProvider extends DartAssist { '([\n${''.padLeft(lineIndent)}${lines.map((s) => ' $s').join('\n')},\n${''.padLeft(lineIndent)}])'; void wrapWith(String name, [List? suggestions]) { - final cb = reporter.createChangeBuilder( - priority: 2, - message: 'Wrap with $name', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 2, message: 'Wrap with $name').addDartFileEdit((builder) { builder.addReplacement(node.sourceRange, (edit) { if (suggestions != null) { - edit.addSimpleLinkedEdit(name, suggestions.first, - kind: LinkedEditSuggestionKind.METHOD, suggestions: suggestions); + edit.addSimpleLinkedEdit( + name, + suggestions.first, + kind: LinkedEditSuggestionKind.METHOD, + suggestions: suggestions, + ); } else { edit.write(name); } @@ -95,8 +89,13 @@ class TreeAssistProvider extends DartAssist { }); } - void removeComponent(CustomLintResolver resolver, ChangeReporter reporter, Expression node, int lineIndent, - ArgumentList argumentList) { + void removeComponent( + CustomLintResolver resolver, + ChangeReporter reporter, + Expression node, + int lineIndent, + ArgumentList argumentList, + ) { var children = [ for (var arg in argumentList.arguments) if (arg is NamedExpression) @@ -165,12 +164,7 @@ class TreeAssistProvider extends DartAssist { var content = resolver.source.contents.data; var source = content.substring(node.offset, node.end).reIndent(4 - lineIndent, skipFirst: true); - final cb = reporter.createChangeBuilder( - priority: 4, - message: 'Extract component', - ); - - cb.addDartFileEdit((builder) { + reporter.createChangeBuilder(priority: 4, message: 'Extract component').addDartFileEdit((builder) { builder.addInsertion(resolver.source.contents.data.length, (edit) { edit.write('\nclass '); edit.addSimpleLinkedEdit('name', 'MyComponent');