Code generation and build.yaml
The builder
Section titled “The builder”gisila ships one build_runner builder, declared in the package’s own build.yaml:
builders: gisila: import: "package:gisila/builder.dart" builder_factories: ["controllerBuilder"] build_extensions: ".dart": - ".gisila.g.part" auto_apply: dependents build_to: cache applies_builders: ["source_gen|combining_builder"]auto_apply: dependents: it runs in every package that depends on gisila, with no configuration.- It is a
source_genshared-part builder: its output is merged bysource_gen’s combining builder into<file>.g.dart, next to output from othersource_gengenerators (such asjson_serializable). - It takes no builder options.
To use it, add build_runner as a dev dependency, add a part directive to each controller file, and run the build:
dev_dependencies: build_runner: ">=2.5.0 <3.0.0"import 'package:gisila/gisila.dart';
part 'users_controller.g.dart';dart run build_runner build --delete-conflicting-outputsdart run build_runner watch --delete-conflicting-outputs # while developingRestricting where it runs
Section titled “Restricting where it runs”On large projects you can limit the builder to your controller files with standard build.yaml target configuration in your package:
targets: $default: builders: gisila:gisila: generate_for: - lib/controllers/**What gets generated
Section titled “What gets generated”For each class annotated with @Controller that has at least one route method, the generator emits an extension:
extension UsersControllerGisilaWeb on UsersController { void attachToApp(GisilaApp app, Router router, {String prefix = ''}) { // one block per route: { final basePath = '$prefix/users/<id>'; final RouteConfig __cfg = RouteConfig.empty .merge(const RouteConfig(requireAuth: true, permissions: const <String>{'users:read'})); final __route = gisilaRoute( app: app, config: __cfg, routeId: 'GET $basePath', handler: (RequestContext ctx) async { final request = ctx.request; final (id, users) = await (() async { try { final id = coerce<String>(request.params['id'], 'id', required: true); final users = ctx.service<UserService>(); return (id, users); } on TypeError catch (e) { throw BadRequestException('Invalid request payload ($e)'); } on FormatException catch (e) { throw BadRequestException('Invalid request format: ${e.message}'); } })(); final result = await this.getUser(id, users); return jsonResponse(body: result, statusCode: 200); }, ); router.get(basePath, __route); } }}Every route block:
- Joins the controller and method paths and converts
{name}to<name>. - Builds the route’s
RouteConfigby merging@UseRouteConfig, the permission annotations, the annotation’sconfig:and@UseMiddlewarenames (looked up withapp.namedMiddleware). - Binds parameters as described in parameter binding rules, turning
TypeError/FormatExceptionduring binding into400. - Calls your method and renders its return value.
- Registers the route on the router through
gisilaRoute, plus a slash-less alias for paths that end in/.
Build errors you may see:
| Error | Cause |
|---|---|
@Controller must be placed on a class. |
The annotation is on something else. |
could not classify parameter "x" |
A second body-like parameter. Annotate it with @Path, @Query, @Header or @Body, make it a Form or Service subtype, or remove it. |
Warning: has no methods carrying @Get/@Post/... |
The class is skipped. |
Annotations are matched by the library that declares them, so classes named Public or Roles from other packages are never mistaken for gisila’s.
controllerBuilder
Section titled “controllerBuilder”Builder controllerBuilder(BuilderOptions _)From package:gisila/builder.dart. The factory build.yaml refers to. You never call it yourself.
Builder helpers
Section titled “Builder helpers”package:gisila/builder_helpers.dart re-exports build-time helpers for generators that compose with gisila’s annotations (notably gisila_doc). Application code doesn’t need it. Types such as DartType, DartObject and ClassElement come from package:analyzer.
Route config literals
Section titled “Route config literals”| Function | Description |
|---|---|
String emitRouteConfigLiteral(DartObject? obj) |
Dart source that rebuilds a constant RouteConfig read from an annotation (rate limit, timeout, body cap, content types, CORS, guards, status code, public flag). 'RouteConfig.empty' for null or an empty config. |
String emitGuardsRouteConfigLiteral({required bool requireAuth, required Set<String> permissions, required Set<String> anyPermission, required Set<String> roles, required bool isPublic}) |
A RouteConfig literal carrying only guard fields. |
String? emitNamedMiddlewareConfig(Iterable<String> names) |
A RouteConfig(middleware: [app.namedMiddleware(...)]) literal, or null when names is empty. |
Type and string utilities
Section titled “Type and string utilities”| Function | Description |
|---|---|
bool isNullable(DartType type) |
T? or Null. |
String typeDisplayNameNullable(DartType type) |
Display name, keeping ?. |
String typeDisplayName(DartType type) |
Display name without ?. |
bool isCoercible(DartType type) |
String, int, double, num, bool or DateTime. |
bool isShelfRequest(DartType type) |
Shelf’s Request. |
bool isShelfResponse(DartType type) |
Shelf’s Response. |
bool isRequestContext(DartType type) |
gisila’s RequestContext. |
bool isFormData(DartType type) |
gisila’s FormData. |
bool isUploadedFile(DartType type) |
gisila’s UploadedFile. |
bool isFormSubtype(DartType type) |
A subclass of gisila’s Form. |
bool isServiceSubtype(DartType type) |
A subclass of gisila’s Service. |
DartType unwrapFuture(DartType type) |
T for Future<T>, otherwise type. |
bool isVoid(DartType type) |
void or Future<void>. |
bool isStringMap(DartType type) |
A Map with String keys. |
DartType? listElementType(DartType type) |
T for List<T>, else null. |
Iterable<String> pathPlaceholders(String path) |
Names of {placeholder} segments. |
String toShelfPath(String path) |
/users/{id} to /users/<id>. |
ConstructorElement? fromJsonConstructor(ClassElement cls) |
A one-argument fromJson(Map) constructor, if any. |
ConstructorElement? fromFormConstructor(ClassElement cls) |
A one-argument fromForm(Map) constructor, if any. |
bool isGisilaLibrary(LibraryElement library) |
The library belongs to package:gisila. |
String dartString(String value) |
Quotes a value as a single-quoted Dart string literal. |
String joinPath(String prefix, String suffix) |
Joins two path parts with one slash. |
String stringSetLiteral(Iterable<String> values) |
A const <String>{...} literal. |
See also: Code generation concept.