start method

Future<HttpServer> start({
  1. int? port,
})

Sobe o servidor. Se port for nulo, usa a variável de ambiente PORT (definida pelo jaspr serve/jaspr build) ou 8080.

Implementation

Future<HttpServer> start({int? port}) async {
  final resolvedPort =
      port ?? int.tryParse(Platform.environment['PORT'] ?? '') ?? 8080;
  final meta = MetaTagsBuilder(seo);
  final router = Router();

  // Geração estática: reporta TODAS as rotas + SEO + 404 ao proxy do
  // jaspr_cli na 1ª requisição (dentro do handler, aguardado antes da
  // resposta). Assim a fila enche antes do crawler esvaziá-la — evita a
  // corrida que deixava só a raiz gerada.
  var reportedAll = false;
  Future<void> reportGenerateRoutes() async {
    if (reportedAll) return;
    reportedAll = true;
    try {
      for (final m in await registry.resolveAll()) {
        await ServerApp.requestRouteGeneration(m.path, priority: m.priority);
      }
      for (final extra in const [
        '/robots.txt',
        '/sitemap.xml',
        '/llms.txt',
        '/llms-full.txt',
        '/404.html',
      ]) {
        await ServerApp.requestRouteGeneration(extra);
      }
    } catch (_) {
      // Proxy pode fechar durante o shutdown — ignora.
    }
  }

  // /robots.txt, /sitemap.xml, /llms.txt, /llms-full.txt automáticos.
  SeoEndpoints(config: seo, registry: registry).mountOn(router);

  // APIs declarativas (envelope/validação/paginação/auth padrão).
  if (apis.isNotEmpty && db != null) {
    FlenxApi(
      apis,
      db!,
      onEmail: onEmail,
      tokenVerifier: tokenVerifier,
    ).mountOn(router);
  }

  router.mount(
    '/',
    serveApp((request, render) async {
      if (kGenerateMode) await reportGenerateRoutes();
      final path = request.requestedUri.path.isEmpty
          ? '/'
          : request.requestedUri.path;
      // `/404.html`: serve a página notFound com status 200 (necessário para o
      // build estático gravar 404.html — o crawler ignora respostas != 200).
      final is404Page = path == '/404.html';
      final result = is404Page
          ? null
          : await resolvePage(path, request.requestedUri.queryParameters);
      final known = result != null;

      final routeMeta =
          await registry.find(path) ??
          (known
              ? RouteMeta(
                  path: path,
                  title: seo.siteName,
                  description: seo.description,
                )
              : const RouteMeta(
                  path: '/404',
                  title: '404 — Página não encontrada',
                  description: 'A página que você procura não existe.',
                  noindex: true,
                ));

      final document = Document(
        title: routeMeta.title,
        lang: lang,
        styles: [..._resetStyles, ...styleRules],
        head: [
          ...meta.build(routeMeta),
          for (final raw in rawStyles)
            Component.element(tag: 'style', children: [RawText(raw)]),
          for (final img in preloadImages)
            link(
              rel: 'preload',
              href: img,
              attributes: const {'as': 'image', 'fetchpriority': 'high'},
            ),
          ...headExtra,
          if (result?.island ?? false)
            script(src: flutterBootstrap, defer: true),
        ],
        body: floatingButtons.isEmpty
            ? (result?.body ?? notFound)
            : Component.fragment([
                result?.body ?? notFound,
                ...floatingButtons,
              ]),
      );

      final response = await render(document);
      if (known || is404Page) return response;
      final html = await response.readAsString();
      return Response.notFound(
        html,
        headers: {'content-type': 'text/html; charset=utf-8'},
      );
    }),
  );

  final handler = const Pipeline()
      .addMiddleware(logRequests())
      .addHandler(router.call);

  // Controle de hot-reload do jaspr: fecha o servidor anterior ao recarregar.
  final reloadLock = _activeReloadLock = Object();
  final server = await serve(
    handler,
    InternetAddress.anyIPv4,
    resolvedPort,
    shared: true,
  );
  if (reloadLock != _activeReloadLock) {
    await server.close();
    return server;
  }
  await _activeServer?.close();
  _activeServer = server;

  // Geração estática (`jaspr build` em mode: static): destrava o crawler
  // reportando a raiz (as demais rotas são reportadas na 1ª requisição, no
  // handler) e escreve o .htaccess na pasta de saída.
  if (kGenerateMode) {
    await ServerApp.requestRouteGeneration('/');
    if (emitHtaccess) {
      const outDir = String.fromEnvironment(
        'jaspr.dev.web',
        defaultValue: 'build/jaspr',
      );
      try {
        File('$outDir/.htaccess').writeAsStringSync(_htaccess);
      } catch (_) {
        // Ambiente sem acesso de escrita — ignora silenciosamente.
      }
    }
  }
  return server;
}