mergeMagicInto static method

String mergeMagicInto(
  1. String existingSource, {
  2. required String appName,
  3. required List<String> configImports,
  4. required List<String> configFactories,
})

Merges Magic into existingSource.

@param existingSource Current contents of lib/main.dart. @param appName Title-Cased app name (extracted from pubspec). @param configImports Import statements for each magic config file (e.g. "import 'config/app.dart';"). @param configFactories Factory expressions for Magic.init's configFactories: list (e.g. '() => appConfig'). @return Transformed source with Magic injected. @throws FormatException When main() is not async. @throws StateError When the source has no runApp( call.

Implementation

static String mergeMagicInto(
  String existingSource, {
  required String appName,
  required List<String> configImports,
  required List<String> configFactories,
}) {
  // 1. Reject sync main() with an actionable message naming both alternatives.
  if (_asyncMainPattern.firstMatch(existingSource) == null) {
    throw FormatException(
      'main() must be async to use Magic. Convert: '
      '`void main() async { ... }` then re-run install --preserve. '
      'To replace entirely, use --force.',
    );
  }

  var result = existingSource;

  // 2. Add imports (Magic + Wind + each configImport) idempotently. Each
  //    call to injectBeforeAnchor returns the source unchanged when the
  //    snippet is already present, so re-runs do not duplicate.
  const magicImport = "import 'package:magic/magic.dart';";
  const windImport = "import 'package:fluttersdk_wind/fluttersdk_wind.dart';";
  const flutterMaterialAnchor = "import 'package:flutter/material.dart'";

  result = MainDartEditor.injectBeforeAnchor(
    source: result,
    anchor: flutterMaterialAnchor,
    snippet: '$magicImport\n',
  );
  result = MainDartEditor.injectBeforeAnchor(
    source: result,
    anchor: flutterMaterialAnchor,
    snippet: '$windImport\n',
  );
  for (final imp in configImports) {
    result = MainDartEditor.injectBeforeAnchor(
      source: result,
      anchor: flutterMaterialAnchor,
      snippet: '$imp\n',
    );
  }

  // 3. Inject `await Magic.init(...)` immediately after the `main() async {`
  //    line. Skip when an existing call is already present (idempotency).
  if (!result.contains('await Magic.init(')) {
    result = _injectMagicInitAfterMainOpen(result, configFactories);
  }

  // 4. Wrap `runApp(X)` with `MagicApplication(child: X, appName: '...')`.
  //    Skip when MagicApplication already wraps the call (idempotency).
  if (!result.contains('MagicApplication(')) {
    result = _wrapRunAppWithMagicApplication(result, appName);
  }

  return result;
}