Skip to content

Code generation and build.yaml

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_gen shared-part builder: its output is merged by source_gen’s combining builder into <file>.g.dart, next to output from other source_gen generators (such as json_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';
Terminal window
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch --delete-conflicting-outputs # while developing

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/**

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:

  1. Joins the controller and method paths and converts {name} to <name>.
  2. Builds the route’s RouteConfig by merging @UseRouteConfig, the permission annotations, the annotation’s config: and @UseMiddleware names (looked up with app.namedMiddleware).
  3. Binds parameters as described in parameter binding rules, turning TypeError/FormatException during binding into 400.
  4. Calls your method and renders its return value.
  5. 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.

Builder controllerBuilder(BuilderOptions _)

From package:gisila/builder.dart. The factory build.yaml refers to. You never call it yourself.

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.

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.
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.