Skip to content

Commit 456b66b

Browse files
authored
Fuse all plugins (#58)
1 parent 934de96 commit 456b66b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+5548
-3640
lines changed

Diff for: .github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
run: flutter format --set-exit-if-changed .
4848

4949
- name: Analyze
50-
run: flutter analyze
50+
run: dart analyze
5151

5252
# - name: Run tests
5353
# run: melos exec --dir-exists=test "dart test"

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ pubspec.lock
66
# Ignoring generated files, as they pollute pull requests and can create merge conflicts
77
*.g.dart
88
*.freezed.dart
9+
# Including generated files from the lib folders, as these should be published on pub
910
!packages/*/lib/**/*.freezed.dart
11+
!packages/*/lib/**/*.g.dart
1012

1113
# Ignoring native folders of the example as they can be re-generated easily
1214
**/example/android/

Diff for: README.md

+19-30
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
- [Creating a custom lint package](#creating-a-custom-lint-package)
2525
- [Using our custom lint package in an application](#using-our-custom-lint-package-in-an-application)
2626
- [Obtaining the list of lints in the CI](#obtaining-the-list-of-lints-in-the-ci)
27-
- [Using the Dart debugger and enabling hot-restart.](#using-the-dart-debugger-and-enabling-hot-restart)
27+
- [Using the Dart debugger](#using-the-dart-debugger)
2828
- [Testing your plugins using expect\_lint](#testing-your-plugins-using-expect_lint)
2929
- [FAQs](#faqs)
3030
- [Q. How do I get all the classes present in my source code?](#q-how-do-i-get-all-the-classes-present-in-my-source-code)
@@ -98,9 +98,7 @@ To create a custom lint, you will need two things:
9898

9999
```dart
100100
// This is the entrypoint of our custom linter
101-
void main(List<String> args, SendPort sendPort) {
102-
startPlugin(sendPort, _ExampleLinter());
103-
}
101+
MyPlugin createPlugin() => MyPlugin();
104102
105103
// This class is the one that will analyze Dart files and return lints
106104
class _ExampleLinter extends PluginBase {
@@ -169,39 +167,30 @@ $ dart run custom_lint
169167

170168
If you are working on a Flutter project, run `flutter pub run custom_lint` instead.
171169

172-
### Using the Dart debugger and enabling hot-restart.
173-
174-
To enable hot-restart and use the Dart debugger, we'll want to start plugins in a
175-
slightly different way.
176-
177-
First, create a `tools/custom_lint_debug.dart` file next to `tools/custom_lint.dart`
178-
with the following content:
179-
180-
```dart
181-
import 'dart:io';
182-
import 'package:custom_lint/basic_runner.dart';
183-
184-
void main() {
185-
await customLint(workingDirectory: Directory.current.parent);
186-
}
170+
### Using the Dart debugger
187171

188-
```
172+
To debug plugins in custom_lint, you need to connect to plugins using "attach"
173+
mode in your IDE (`cmd+shift+p` + `Debug: attach to Dart process` in VSCode).
189174

190-
Now you can run/test the custom lints packages you are using via:
175+
When using this command, you will need a VM service URI provided by custom_lint.
191176

192-
- Starting this main from your IDE (such as clicking on `debug` in VScode).
193-
This will connect your IDE to the Dart debugger, allowing the usage of breakpoints.
177+
There are two possible ways to obtain one:
194178

195-
- From the command line with `dart --enable-vm-service path/to/custom_lint_debug.dart`
196-
The Dart debugger will not be enabled, but hot-reload still will be enabled.
197-
Changing the source of your plugin would automatically show the update.
179+
- if you started your plugin using `custom_lint --watch`, it should be visible
180+
in the console output.
181+
- if your plugin is started by your IDE, you can open the `custom_lint.log` file
182+
that custom_lint created next to the `pubspec.yaml` of your analyzed projects.
198183

199-
**I have the following error when I run the program in my IDE: `Bad state: VM service not available! You need to run dart with --enable-vm-service.`**
184+
In both cases, what you're looking for is logs similar to:
200185

201-
Chances are you didn't start your program in debug mode.
202-
If using VScode, when starting your program, make sure to click on `debug` not `run`:
186+
```
187+
The Dart VM service is listening on http://127.0.0.1:60671/9DS43lRMY90=/
188+
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:60671/9DS43lRMY90=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A60671%2F9DS43lRMY90%3D%2Fws
189+
```
203190

204-
![VScode's debug button](https://raw.githubusercontent.com/invertase/dart_custom_lint/main/resources/vscode_debug.jpg)
191+
What you'll want is the first URI. In this example, that is `http://127.0.0.1:60671/9DS43lRMY90=/`.
192+
You can then pass this to your IDE, which should now be able to attach to the
193+
plugin.
205194

206195
### Testing your plugins using expect_lint
207196

Diff for: analysis_options.yaml

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ analyzer:
33
exclude:
44
- "**/*.g.dart"
55
- "**/*.freezed.dart"
6-
strong-mode:
7-
implicit-casts: false
8-
implicit-dynamic: false
6+
language:
7+
strict-casts: true
8+
strict-inference: true
9+
strict-raw-types: true
910
errors:
1011
# Otherwise cause the import of all_lint_rules to warn because of some rules conflicts.
1112
# We explicitly enabled even conflicting rules and are fixing the conflict
@@ -16,6 +17,8 @@ analyzer:
1617

1718
linter:
1819
rules:
20+
public_member_api_docs: false
21+
1922
# temporarily disabled
2023
require_trailing_commas: false
2124

Diff for: packages/custom_lint/CHANGELOG.md

+25
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
## Unreleased major
2+
3+
- **Breaking**: The plugin entrypoint has moved.
4+
Plugins no-longer should define a `/bin/custom_lint.dart` file.
5+
Instead they should define a `/lib/<my_package_name>.dart`
6+
7+
- **Breaking**: The plugin entrypoint is modified. Plugins no-longer
8+
define a "main", but instead define a `createPlugin` function:
9+
10+
Before:
11+
12+
```dart
13+
// /bin/custom_lint.dart
14+
void main(List<String> args, SendPort sendPort) {
15+
startPlugin(sendPort, MyPlugin());
16+
}
17+
```
18+
19+
After:
20+
21+
```dart
22+
// /lib/<my_package_name.dart
23+
MyPlugin createPlugin() => MyPlugin();
24+
```
25+
126
## 0.0.16
227

328
Fix `expect_lint` not working if the file doesn't contain any lint.

Diff for: packages/custom_lint/bin/custom_lint.dart

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import 'dart:async';
22
import 'dart:io';
33

44
import 'package:args/args.dart';
5-
import 'package:custom_lint/basic_runner.dart';
5+
import 'package:custom_lint/custom_lint.dart';
66

7-
Future<void> main([List<String> args = const []]) async {
7+
Future<void> entrypoint([List<String> args = const []]) async {
88
final parser = ArgParser()
99
..addFlag(
1010
'watch',
@@ -30,3 +30,9 @@ Future<void> main([List<String> args = const []]) async {
3030

3131
await customLint(workingDirectory: Directory.current, watchMode: watchMode);
3232
}
33+
34+
void main([List<String> args = const []]) async {
35+
await entrypoint(args);
36+
// TODO figure out why this exit is necessary
37+
exit(exitCode);
38+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
include: ../analysis_options.yaml
1+
# include: ../analysis_options.yaml
22

3-
linter:
4-
rules:
5-
public_member_api_docs: false
6-
avoid_print: false
3+
# linter:
4+
# rules:
5+
# public_member_api_docs: false
6+
# avoid_print: false

Diff for: packages/custom_lint_builder/example/example_lint/bin/custom_lint.dart renamed to packages/custom_lint/example/example_lint/lib/custom_lint_example_lint.dart

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:isolate';
2-
31
import 'package:analyzer/dart/analysis/results.dart';
42
import 'package:analyzer/dart/element/element.dart';
53
import 'package:analyzer/dart/element/type.dart';
@@ -19,15 +17,13 @@ bool _isProvider(DartType type) {
1917
return isProviderBase || element.allSupertypes.any(_isProvider);
2018
}
2119

22-
void main(List<String> args, SendPort sendPort) {
23-
startPlugin(sendPort, _RiverpodLint());
24-
}
20+
PluginBase createPlugin() => _RiverpodLint();
2521

2622
class _RiverpodLint extends PluginBase {
2723
@override
2824
Stream<Lint> getLints(ResolvedUnitResult resolvedUnitResult) async* {
2925
final library = resolvedUnitResult.libraryElement;
30-
print('This is a print');
26+
print('This is a print3');
3127
final providers = library.topLevelElements
3228
.whereType<VariableElement>()
3329
.where((e) => !e.isFinal)

Diff for: packages/custom_lint/example/example_lint/tools/custom_lint_debug.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'dart:io';
2-
import 'package:custom_lint/basic_runner.dart';
2+
import 'package:custom_lint/custom_lint.dart';
33

44
Future<void> main() async {
55
await customLint(workingDirectory: Directory.current.parent);

Diff for: packages/custom_lint/lib/basic_runner.dart

+3-182
Original file line numberDiff line numberDiff line change
@@ -1,183 +1,4 @@
1-
import 'dart:async';
2-
import 'dart:convert';
3-
import 'dart:io';
1+
@Deprecated('Import `package:custom_lint/custom_lint.dart` instead')
2+
library basic_runner;
43

5-
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
6-
import 'package:path/path.dart' as p;
7-
8-
import 'src/analyzer_plugin/analyzer_plugin.dart';
9-
import 'src/analyzer_plugin/plugin_delegate.dart';
10-
import 'src/protocol/internal_protocol.dart';
11-
import 'src/runner.dart';
12-
13-
const _help = '''
14-
15-
Custom lint runner commands:
16-
r: Force re-lint
17-
q: Quit
18-
19-
''';
20-
21-
/// Runs plugins with custom_lint.dart on the given directory
22-
///
23-
/// In watch mode:
24-
/// * This will run until the user types q to quit
25-
/// * The plugin will hot-reload when the user changes it's code, and will cause a re-lint
26-
/// * The exit code is the one from the last lint before quitting
27-
/// * The user can force a reload by typing r
28-
///
29-
/// Otherwise:
30-
/// * There is no hot-reload or watching so linting only happens once
31-
/// * The process exits with the most recent result of the linter
32-
///
33-
/// Watch mode cannot be enabled if in release mode.
34-
Future<void> customLint({
35-
bool watchMode = true,
36-
required Directory workingDirectory,
37-
}) async {
38-
await runZonedGuarded(() async {
39-
final runner = CustomLintRunner(
40-
CustomLintPlugin(
41-
delegate: CommandCustomLintDelegate(),
42-
includeBuiltInLints: false,
43-
watchMode: watchMode,
44-
),
45-
workingDirectory,
46-
);
47-
48-
runner.channel
49-
..responseErrors.listen((event) => exitCode = -1)
50-
..pluginErrors.listen((event) => exitCode = -1)
51-
..notifications.listen((event) async {
52-
if (watchMode) {
53-
switch (event.event) {
54-
case PrintNotification.key:
55-
final notification = PrintNotification.fromNotification(event);
56-
stdout.writeln(notification.message);
57-
break;
58-
case DidHotReloadNotification.key:
59-
stdout.writeln(
60-
'Did hot-reload the sources of a plugin.\nRecomputing lints...',
61-
);
62-
await _runPlugins(runner, reload: true);
63-
stdout.writeln(_help);
64-
break;
65-
}
66-
}
67-
});
68-
69-
try {
70-
await runner.initialize();
71-
await _runPlugins(runner, reload: false);
72-
73-
if (watchMode) {
74-
await _startWatchMode(runner);
75-
}
76-
} finally {
77-
await runner.close();
78-
}
79-
}, (err, stack) {
80-
exitCode = -1;
81-
stderr.writeln('$err\n$stack');
82-
});
83-
}
84-
85-
Future<void> _runPlugins(
86-
CustomLintRunner runner, {
87-
required bool reload,
88-
}) async {
89-
// Reset the code
90-
exitCode = 0;
91-
92-
try {
93-
final lints = await runner.getLints(reload: reload);
94-
95-
if (lints.any((lintsForFile) => lintsForFile.errors.isNotEmpty)) {
96-
exitCode = -1;
97-
}
98-
99-
_renderLints(lints, workingDirectory: runner.workingDirectory);
100-
} catch (err, stack) {
101-
exitCode = -1;
102-
stderr.writeln('$err\n$stack');
103-
}
104-
105-
// Since no problem happened, we print a message saying everything went well
106-
if (exitCode == 0) {
107-
stdout.writeln('No issues found!');
108-
}
109-
}
110-
111-
void _renderLints(
112-
List<AnalysisErrorsParams> lints, {
113-
required Directory workingDirectory,
114-
}) {
115-
lints.sort(
116-
(a, b) => a
117-
.relativeFilePath(workingDirectory)
118-
.compareTo(b.relativeFilePath(workingDirectory)),
119-
);
120-
121-
for (final lintsForFile in lints) {
122-
final relativeFilePath = lintsForFile.relativeFilePath(workingDirectory);
123-
124-
lintsForFile.errors.sort((a, b) {
125-
final lineCompare = a.location.startLine.compareTo(b.location.startLine);
126-
if (lineCompare != 0) return lineCompare;
127-
final columnCompare =
128-
a.location.startColumn.compareTo(b.location.startColumn);
129-
if (columnCompare != 0) return columnCompare;
130-
131-
final codeCompare = a.code.compareTo(b.code);
132-
if (codeCompare != 0) return codeCompare;
133-
134-
return a.message.compareTo(b.message);
135-
});
136-
137-
for (final lint in lintsForFile.errors) {
138-
exitCode = -1;
139-
stdout.writeln(
140-
' $relativeFilePath:${lint.location.startLine}:${lint.location.startColumn}'
141-
' • ${lint.message} • ${lint.code}',
142-
);
143-
}
144-
}
145-
}
146-
147-
Future<void> _startWatchMode(CustomLintRunner runner) async {
148-
if (stdin.hasTerminal) {
149-
stdin
150-
// Let's not pollute the output with whatever the user types
151-
..echoMode = false
152-
// Let's not force user to have to press "enter" to input a command
153-
..lineMode = false;
154-
}
155-
156-
stdout.writeln(_help);
157-
158-
// Handle user inputs, forcing the command to continue until the user asks to "quit"
159-
await for (final input in stdin.transform(utf8.decoder)) {
160-
switch (input) {
161-
case 'r':
162-
// Reruning lints
163-
stdout.writeln('Manual Reload...');
164-
await _runPlugins(runner, reload: true);
165-
break;
166-
case 'q':
167-
// Let's quit the command line
168-
// TODO(rrousselGit) Investigate why an "exit" is required and we can't simply "return"
169-
exit(exitCode);
170-
default:
171-
// Unknown command. Nothing to do
172-
}
173-
}
174-
}
175-
176-
extension on AnalysisErrorsParams {
177-
String relativeFilePath(Directory dir) {
178-
return p.relative(
179-
file,
180-
from: dir.path,
181-
);
182-
}
183-
}
4+
export 'custom_lint.dart';

0 commit comments

Comments
 (0)