Skip to content

Commit 2396e30

Browse files
committed
Feat: Add dart_grog_lint package
closes VeryGoodOpenSource#548
1 parent 5f0d4b6 commit 2396e30

29 files changed

+596
-0
lines changed

Diff for: packages/dart_frog_lint/.gitignore

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# See https://www.dartlang.org/guides/libraries/private-files
2+
3+
# Files and directories created by pub
4+
.dart_tool/
5+
.packages
6+
build/
7+
pubspec.lock
8+
9+
# Test related files
10+
coverage/

Diff for: packages/dart_frog_lint/CHANGELOG.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# 0.3.3
2+
3+
- deps: upgrade to `Dart ">=2.19.0 <3.0.0"`
4+
- deps: upgrade to `very_good_analysis ^4.0.0`
5+
6+
# 0.3.2
7+
8+
- feat: cache `Request` and `Response` body
9+
10+
# 0.3.1
11+
12+
- feat: add `formData` to `Request`/`Response`
13+
14+
# 0.3.0
15+
16+
- **BREAKING** fix: `Request.json()` and `Response.json()` return `Future<dynamic>`
17+
18+
# 0.2.0
19+
20+
- **BREAKING** feat: support mounting dynamic routes
21+
- **BREAKING** deps: upgrade to `Dart ">=2.18.0 <3.0.0"`
22+
- deps: upgrade to `very_good_analysis ^3.1.0`
23+
24+
# 0.1.2
25+
26+
- feat: add x-powered-by-header to `serve`
27+
28+
# 0.1.1
29+
30+
- fix: update `Response.json` headers to `<String, Object>`
31+
32+
# 0.1.0
33+
34+
- chore: stable 0.1.0 release
35+
36+
# 0.0.1-dev.12
37+
38+
- feat: expose `HttpConnectionInfo` on `Request`
39+
- chore: upgrade to very_good_analysis v3.0.1
40+
41+
# 0.0.1-dev.11
42+
43+
- fix: Request/Response `headers` is of type `Map<String, String>`
44+
- docs: pubspec `homepage`, `repository`, `issue_tracker`, and `documentation` links
45+
46+
# 0.0.1-dev.10
47+
48+
- fix: Response `json()` returns `Object?`
49+
- fix: provider `StateError` message typo
50+
51+
# 0.0.1-dev.9
52+
53+
- feat: expose `fromShelfHandler` and `fromShelfMiddleware` adapters
54+
55+
# 0.0.1-dev.8
56+
57+
- feat: add `createStaticFileHandler`
58+
- feat: add `Cascade`
59+
60+
# 0.0.1-dev.7
61+
62+
- fix: hot reload stability improvements and error reporting
63+
64+
# 0.0.1-dev.6
65+
66+
- feat: expand router http method support
67+
68+
# 0.0.1-dev.5
69+
70+
- feat: change `Response.json` body to type `Object?`
71+
72+
# 0.0.1-dev.4
73+
74+
- fix: support multiple routeNotFound.read calls
75+
- resolves: `bad state: The 'read' method can only be called once`
76+
77+
# 0.0.1-dev.3
78+
79+
- docs: add example and improve documentation
80+
81+
# 0.0.1-dev.2
82+
83+
- docs: fix README assets
84+
85+
# 0.0.1-dev.1
86+
87+
- feat: initial experimental release 🎉

Diff for: packages/dart_frog_lint/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Very Good Ventures
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: packages/dart_frog_lint/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
WIP

Diff for: packages/dart_frog_lint/analysis_options.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include: package:very_good_analysis/analysis_options.4.0.0.yaml

Diff for: packages/dart_frog_lint/coverage_badge.svg

+20
Loading

Diff for: packages/dart_frog_lint/example/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
custom_lint.log
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include: ../analysis_options.yaml
2+
analyzer:
3+
plugins:
4+
- custom_lint
5+
linter:
6+
rules:
7+
file_names: false

Diff for: packages/dart_frog_lint/example/lib/out.dart

Whitespace-only changes.

Diff for: packages/dart_frog_lint/example/main.dart

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'dart:io';
2+
3+
import 'package:dart_frog/dart_frog.dart';
4+
5+
Future<HttpServer> run(Handler handler, InternetAddress ip, int port) {
6+
throw UnimplementedError();
7+
}

Diff for: packages/dart_frog_lint/example/pubspec.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: dart_frog_lint_example
2+
version: 0.0.1
3+
publish_to: "none"
4+
5+
environment:
6+
sdk: ">=2.17.0 <3.0.0"
7+
8+
dependencies:
9+
dart_frog:
10+
path: ../../dart_frog
11+
12+
dev_dependencies:
13+
dart_frog_lint:
14+
path: ..
15+
very_good_analysis: ^4.0.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_request
4+
Response onRequest(String context) {
5+
return Response();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_middleware
4+
Handler middleware(int handler) {
5+
throw UnimplementedError();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_request
4+
String onRequest(Request context) {
5+
return '';
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_middleware
4+
String middleware(Handler handler) {
5+
throw UnimplementedError();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// expect_lint: dart_frog_middleware
2+
import 'dart:async';
3+
4+
FutureOr<void> fn() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// expect_lint: dart_frog_request
2+
import 'dart:async';
3+
4+
FutureOr<void> fn() {}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_request
4+
Response onRequest() {
5+
return Response();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// expect_lint: dart_frog_middleware
4+
Handler middleware() {
5+
throw UnimplementedError();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// Incorrect parameter type
4+
// expect_lint: dart_frog_request
5+
Response onRequest(RequestContext context, int userId2) {
6+
return Response();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
// Missing parameter
4+
// expect_lint: dart_frog_request
5+
Response onRequest(RequestContext context) {
6+
return Response();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import 'package:dart_frog/dart_frog.dart';
2+
3+
Handler middleware(Handler handler) {
4+
return handler;
5+
}

Diff for: packages/dart_frog_lint/lib/dart_frog_lint.dart

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'package:custom_lint_builder/custom_lint_builder.dart';
2+
import 'package:dart_frog_lint/src/dart_frog_entrypoint.dart';
3+
import 'package:dart_frog_lint/src/dart_frog_middleware.dart';
4+
import 'package:dart_frog_lint/src/dart_frog_request.dart';
5+
6+
/// The entrypoint of dart_frog_lint
7+
PluginBase createPlugin() => _DartFrogLintPlugin();
8+
9+
class _DartFrogLintPlugin extends PluginBase {
10+
@override
11+
List<LintRule> getLintRules(CustomLintConfigs configs) => [
12+
const DartFrogRequest(),
13+
const DartFrogMiddleware(),
14+
const DartFrogEntrypoint(),
15+
];
16+
}
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import 'package:analyzer/dart/ast/ast.dart';
2+
import 'package:analyzer/dart/element/type.dart';
3+
import 'package:analyzer/error/listener.dart';
4+
import 'package:collection/collection.dart';
5+
import 'package:custom_lint_builder/custom_lint_builder.dart';
6+
import 'package:dart_frog_lint/src/types.dart';
7+
8+
/// {@template dart_frog_lint.request}
9+
/// The definition of `dart_frog_entrypoint` lints.
10+
/// {@endtemplate}
11+
class DartFrogEntrypoint extends DartLintRule {
12+
/// {@macro dart_frog_lint.request}
13+
const DartFrogEntrypoint()
14+
: super(
15+
code: const LintCode(
16+
name: 'dart_frog_entrypoint',
17+
problemMessage: 'Main files should define a valid "run" function.',
18+
),
19+
);
20+
21+
@override
22+
List<String> get filesToAnalyze => ['main.dart'];
23+
24+
@override
25+
void run(
26+
CustomLintResolver resolver,
27+
ErrorReporter reporter,
28+
CustomLintContext context,
29+
) {
30+
context.registry.addCompilationUnit((node) {
31+
// Search for a function declaration with the name "run"
32+
final run =
33+
node.declarations.whereType<FunctionDeclaration>().firstWhereOrNull(
34+
(declaration) => declaration.name.lexeme == 'run',
35+
);
36+
37+
if (run == null) {
38+
// No function declaration found with the name "run"
39+
reporter.reportErrorForNode(code, node.directives.firstOrNull ?? node);
40+
return;
41+
}
42+
43+
if (run.functionExpression.parameters?.parameters.length != 3) {
44+
// Only one parameter is allowed
45+
reporter.reportErrorForNode(code, run);
46+
return;
47+
}
48+
49+
final handlerType = run.functionExpression.parameters?.parameters
50+
.firstOrNull?.declaredElement?.type;
51+
final ipType = run.functionExpression.parameters?.parameters
52+
.elementAtOrNull(1)
53+
?.declaredElement
54+
?.type;
55+
final portType = run.functionExpression.parameters?.parameters
56+
.elementAtOrNull(2)
57+
?.declaredElement
58+
?.type;
59+
60+
if (handlerType == null || !isHandler(handlerType)) {
61+
// The parameter is not a Handler
62+
reporter.reportErrorForNode(code, run);
63+
return;
64+
}
65+
66+
if (ipType == null || !internetAddressTypeChecker.isExactlyType(ipType)) {
67+
// The parameter is not a Handler
68+
reporter.reportErrorForNode(code, run);
69+
return;
70+
}
71+
72+
if (portType?.isDartCoreInt != true) {
73+
// The parameter is not a Handler
74+
reporter.reportErrorForNode(code, run);
75+
return;
76+
}
77+
78+
final returnType = run.returnType?.type;
79+
if (returnType == null ||
80+
!returnType.isDartAsyncFuture ||
81+
!httpServerTypeChecker.isExactlyType(
82+
(returnType as InterfaceType).typeArguments.single,
83+
)) {
84+
// The parameter is not a Handler
85+
reporter.reportErrorForNode(code, run);
86+
return;
87+
}
88+
});
89+
}
90+
}

0 commit comments

Comments
 (0)