compile method

Future<CompileResult> compile([
  1. List<Uri>? invalidatedUris
])

Compiles _entrypoint, using an incremental recompile if possible.

invalidatedUris must not be null for all but the very first compile.

The frontend server does not do any of its own invalidation.

Implementation

Future<CompileResult> compile([List<Uri>? invalidatedUris]) async {
  String action;
  switch (_state) {
    case _ClientState.waitingForFirstCompile:
      action = 'compile';
      break;
    case _ClientState.waitingForRecompile:
      action = 'recompile';
      break;
    case _ClientState.waitingForAcceptOrReject:
      throw StateError(
          'Previous `CompileResult` must be accepted or rejected by '
          'calling `accept` or `reject`.');
    case _ClientState.compiling:
      throw StateError(
          'App is already being compiled, you must wait for that to '
          'complete and `accept` or `reject` the result before compiling '
          'again.');
    case _ClientState.rejecting:
      throw StateError('Still waiting for previous `reject` call to finish. '
          'You must await that before compiling again.');
  }
  _state = _ClientState.compiling;

  try {
    var command = StringBuffer('$action $_entrypoint');
    if (action == 'recompile') {
      if (invalidatedUris == null || invalidatedUris.isEmpty) {
        throw StateError(
            'Subsequent compile invocations must provide a non-empty list '
            'of invalidated uris.');
      }
      var boundaryKey = generateUuidV4();
      command.writeln(' $boundaryKey');
      for (var uri in invalidatedUris) {
        command.writeln('$uri');
      }
      command.write(boundaryKey);
    }

    _sendCommand(command.toString());
    var state = _CompileState.started;
    late String feBoundaryKey;
    var newSources = <Uri>{};
    var removedSources = <Uri>{};
    var compilerOutputLines = <String>[];
    var errorCount = 0;
    String? outputDillPath;
    while (
        state != _CompileState.done && await _feServerStdoutLines.hasNext) {
      var line = await _nextInputLine();
      switch (state) {
        case _CompileState.started:
          assert(line.startsWith('result'));
          feBoundaryKey = line.substring(line.indexOf(' ') + 1);
          state = _CompileState.waitingForKey;
          continue;
        case _CompileState.waitingForKey:
          if (line == feBoundaryKey) {
            state = _CompileState.gettingSourceDiffs;
          } else {
            compilerOutputLines.add(line);
          }
          continue;
        case _CompileState.gettingSourceDiffs:
          if (line.startsWith(feBoundaryKey)) {
            state = _CompileState.done;
            var parts = line.split(' ');
            outputDillPath = parts.getRange(1, parts.length - 1).join(' ');
            errorCount = int.parse(parts.last);
            continue;
          }
          var diffUri = Uri.parse(line.substring(1));
          if (line.startsWith('+')) {
            newSources.add(diffUri);
          } else if (line.startsWith('-')) {
            removedSources.add(diffUri);
          } else {
            throw StateError(
                'unrecognized diff line, should start with a + or - but got: $line');
          }
          continue;
        case _CompileState.done:
          throw StateError('Unreachable');
      }
    }

    return CompileResult._(
        dillOutput: outputDillPath,
        errorCount: errorCount,
        newSources: newSources,
        removedSources: removedSources,
        compilerOutputLines: compilerOutputLines);
  } finally {
    _state = _ClientState.waitingForAcceptOrReject;
  }
}