This walks through a complete generic construct, route_list, that writes every route of the server to
.revali/route_list/routes.txt. You need an existing Revali server project. If you do not have one, see
Installation.
1. Create the package#
dart create -t package route_list
cd route_list
dart pub add revali_construct
Only lib/ and pubspec.yaml matter. The rest of the template can go. Add revali_annotations
or revali_router as well if your construct needs to match their annotation types.
2. Implement the construct#
A generic construct extends Construct and returns a RevaliDirectory of files.
MetaServer describes the analyzed server: routes (controllers, each with methods),
apps and public files.
import 'package:revali_construct/revali_construct.dart';
class RouteListConstruct extends Construct {
const RouteListConstruct();
@override
RevaliDirectory generate(RevaliContext context, MetaServer server) {
final lines = [
for (final route in server.routes)
for (final method in route.methods)
'${method.method} /${_join(route.path, method.path)}',
];
return RevaliDirectory(
files: [
AnyFile(basename: 'routes', extension: 'txt', content: lines.join('\n')),
],
);
}
String _join(String controller, String? method) =>
[controller, method ?? ''].where((p) => p.isNotEmpty).join('/');
}
-
AnyFiletakesbasename,extension,content(orbytesfor binary files) andsegmentsfor subdirectories.DartFileandPartFileareAnyFilesubclasses that writepart/part ofdirectives for you. - A
RevaliDirectorymust contain at least one file. -
context.mode(debug, profile or release) andcontext.flavordescribe the current run.
3. Add the entrypoint#
Revali calls a top-level function that takes an optional ConstructOptions
and returns the construct. options.values holds the options: map from the user's
revali.yaml.
import 'package:revali_construct/revali_construct.dart';
import 'package:route_list/src/route_list_construct.dart';
Construct routeListConstruct([ConstructOptions? options]) {
return const RouteListConstruct();
}
4. Register it in construct.yaml#
construct.yaml sits at the package root, next to pubspec.yaml. Its presence is what makes the package a construct.
constructs:
- name: route_list
path: route_list.dart
method: routeListConstruct
| Key | Type | Default | Meaning |
|---|---|---|---|
name |
String |
required |
Identifies the construct in
revali.yaml
and names its output directory,
.revali/<name>/
.
|
path |
String |
required | Entrypoint file, relative to lib/. |
method |
String |
required | Top-level function in that file that returns the construct. |
is_build |
bool |
false |
Makes it a
build construct
: runs only on
revali build
, writes to
.revali/build/
, and
method
must return a
BuildConstruct
.
|
opt_in |
bool |
false |
Skip the construct until the user sets
enabled: true
for it in
revali.yaml
.
|
A package can list several constructs under constructs:. Server generation is built into
revali and cannot be provided by a construct package.
5. Add it to a server#
Constructs must be dev dependencies of the server. Revali ignores packages under dependencies.
dev_dependencies:
route_list:
path: ../route_list
The path is relative to the server's pubspec.yaml. Run dart pub get.
6. Run it#
From the server project:
dart run revali dev --recompile
.revali/
├── server/
└── route_list/
└── routes.txt
GET /users
POST /users
GET /users/:id
--recompile forces Revali to rebuild its cached copy of your construct. Revali detects most edits by itself, but pass the flag whenever a change to your construct does not show up. See
Construct Lifecycle.
Reading options#
Users configure your construct in their revali.yaml:
constructs:
- name: route_list
options:
file_name: endpoints
Construct routeListConstruct([ConstructOptions? options]) {
final fileName = options?.values['file_name'] as String? ?? 'routes';
return RouteListConstruct(fileName: fileName); // pass it to the construct
}
Document every key you read. The other keys of a revali.yaml entry (enabled,
package) are handled by Revali. See Configuring constructs.