build method
Generates the outputs for a given BuildStep.
Implementation
@override
Future<void> build(BuildStep buildStep) async {
// Find all generated .spark.g.part files
final sparkFiles = Glob('lib/**/*.spark.g.part');
final routes = <_RouteInfo>[];
final imports = <String>{};
await for (final file in buildStep.findAssets(sparkFiles)) {
log.info('RouterBuilder found part: ${file.path}');
final content = await buildStep.readAsString(file);
// Parse route info from the generated content
final routeMatches = RegExp(
r"const _\$(\w+)Route = \(([\s\S]*?)\);",
).allMatches(content);
for (final match in routeMatches) {
final className = match.group(1)!;
final routeBody = match.group(2)!;
final path = _extractValue(routeBody, 'path');
final methods = _extractList(routeBody, 'methods');
final pathParams = _extractList(routeBody, 'pathParams');
// Extract the handler function
final handlerMatch = RegExp(
'Future<Response> _\\\$handle$className\\([\\s\\S]*?\\) async \\{[\\s\\S]*?\\n\\}',
).firstMatch(content);
final handlerCode = handlerMatch?.group(0);
if (path != null && handlerCode != null) {
routes.add(
_RouteInfo(
className: className,
path: path,
methods: methods,
pathParams: pathParams,
handlerCode: handlerCode,
),
);
}
}
// Add import for the source file
final sourcePath = file.path.replaceAll('.spark.g.part', '.dart');
imports.add(sourcePath);
}
if (routes.isEmpty) {
// No pages found, generate empty router
await _writeEmptyRouter(buildStep);
return;
}
final buffer = StringBuffer();
// Header
buffer.writeln('// GENERATED CODE - DO NOT MODIFY BY HAND');
buffer.writeln('// Generated by spark_generator');
buffer.writeln('// ignore_for_file: type=lint');
buffer.writeln();
// Imports
buffer.writeln();
buffer.writeln("import 'dart:async';");
buffer.writeln("import 'dart:convert';");
buffer.writeln("import 'dart:io';");
buffer.writeln("import 'dart:isolate';");
buffer.writeln("import 'dart:math';");
buffer.writeln();
buffer.writeln("import 'package:shelf/shelf.dart';");
buffer.writeln("import 'package:shelf/shelf_io.dart' as shelf_io;");
buffer.writeln("import 'package:shelf_router/shelf_router.dart';");
buffer.writeln("import 'package:spark_framework/server.dart';");
buffer.writeln();
// Import source files that contain the page classes
for (final import in imports) {
final importPath = import.replaceFirst('lib/', '');
buffer.writeln(
"import 'package:${buildStep.inputId.package}/$importPath';",
);
}
buffer.writeln();
// Generate helper functions
_writeHelperFunctions(buffer);
// Generate handlers
for (final route in routes) {
buffer.writeln(route.handlerCode);
buffer.writeln();
}
// Generate createSparkRouter
buffer.writeln('/// Creates a router with all registered Spark pages.');
buffer.writeln('///');
buffer.writeln('/// This router contains handlers for:');
for (final route in routes) {
buffer.writeln('/// - `${route.path}` -> ${route.className}');
}
buffer.writeln('Router createSparkRouter() {');
buffer.writeln(' final router = Router();');
buffer.writeln();
// Register each route
for (final route in routes) {
for (final method in route.methods) {
final methodLower = method.toLowerCase();
if (route.pathParams.isEmpty) {
buffer.writeln(
" router.$methodLower('${route.path}', _\$handle${route.className});",
);
} else {
buffer.writeln(
" router.$methodLower('${route.path}', (Request request, ${route.pathParams.map((p) => 'String $p').join(', ')}) =>",
);
buffer.writeln(
' _\$handle${route.className}(request, ${route.pathParams.join(', ')}));',
);
}
}
}
buffer.writeln();
buffer.writeln(' return router;');
buffer.writeln('}');
buffer.writeln();
// Generate SparkServerConfig
_writeServerConfig(buffer);
// Generate createSparkServer
_writeCreateSparkServer(buffer);
// Write output
final outputId = AssetId(
buildStep.inputId.package,
'lib/spark_router.g.dart',
);
await buildStep.writeAsString(outputId, buffer.toString());
}