<commit_before>import 'package:args/command_runner.dart';
import 'package:blog/models/config.dart';
import 'package:blog/site_generator.dart';

class BuildCommand extends Command {
  final name = "build";
  final description = "Build site";
  SiteGenerator _builder;

  void run() async {
    final config = await Config.load();
    _builder = SiteGenerator(config);
    await _builder.build();
  }
}<commit_msg>Add livereload<commit_after>import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:mime/mime.dart' as mime;
import 'package:path/path.dart' as path;
import 'package:blog/models/config.dart';
import 'package:blog/site_generator.dart';

class BuildCommand extends Command {
  final name = "build";
  final description = "Build site";
  SiteGenerator _builder;
  Config _config;

  BuildCommand() {
    argParser.addFlag('preview', callback: (preview) {
      if (preview) {
        print('Preview enabled');
        runServer('_site');
      }
    });
    argParser.addFlag('watch', abbr: 'w', callback: (watch) {
      if (watch) {
        print('Watching file system events');
        io.Directory('.')
            .watch(recursive: true)
            .where((event) => !event.path.startsWith('./_site'))
            .forEach((_) {
          print('Rebuilding site');
          _builder.build();
        });
      }
    });
  }

  void run() async {
    _config = await Config.load();
    _builder = SiteGenerator(_config);
    await _builder.build();
  }

  Future<void> runServer(String basePath) async {
    final server = await io.HttpServer.bind('127.0.0.1', Config.devServerPort);
    print('Server listening on ${server.address.address}:${server.port}');
    await for (io.HttpRequest request in server) {
      await handleRequest(basePath, request);
    }
  }

  Future<void> handleRequest(String basePath, io.HttpRequest request) async {
    final filePath = request.uri.toFilePath();
    final normalizedFilePath = path.posix.extension(filePath).isEmpty
        ? '$filePath/index.html'
        : filePath;
    final file = io.File('$basePath$normalizedFilePath');
    final mimeTypePair = mime.lookupMimeType(normalizedFilePath).split('/');
    if (await file.exists()) {
      request.response.headers.contentType =
          io.ContentType(mimeTypePair[0], mimeTypePair[1], charset: "utf-8");
      await file.openRead().pipe(request.response);
    } else {
      request.response.statusCode = io.HttpStatus.notFound;
      await request.response.close();
    }
  }
}
{'fraction_non_alphanumeric': 0.13968253968253969, 'fraction_numerical': 0.0035714285714285713, 'mean_word_length': 3.35405872193437, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4590276', 'n_tokens_mistral': 766, 'n_tokens_neox': 759, 'n_words': 160}