migrateSource static method

MigrationResult migrateSource(
  1. String source, {
  2. required String className,
  3. required String importUri,
})

Implementation

static MigrationResult migrateSource(
  String source, {
  required String className,
  required String importUri,
}) {
  final notes = <MigrationNote>[];
  final changes = <String>[];
  int lineOf(int index) =>
      '\n'.allMatches(source.substring(0, index)).length + 1;

  final imports = _import.allMatches(source).toList();
  if (imports.isEmpty && !_anyDotenv.hasMatch(source)) {
    return MigrationResult(source, changes, notes);
  }

  // Cases that need a human; the file is left untouched.
  for (final m in imports) {
    if (RegExp(r'\bas\s+\w+').hasMatch(m.group(1) ?? '')) {
      notes.add(MigrationNote(lineOf(m.start),
          'flutter_dotenv is imported with a prefix; update this file by hand.',
          blocking: true));
    }
  }
  for (final m in _mutation.allMatches(source)) {
    notes.add(MigrationNote(lineOf(m.start),
        'Code writes to dotenv.env; the generated values are constant. Update by hand.',
        blocking: true));
  }
  if (notes.any((n) => n.blocking))
    return MigrationResult(source, changes, notes);

  var out = source;

  // 1. `await dotenv.load(...);` statements are no longer needed.
  out = _removeLoads(out, className, changes, notes, lineOf);

  // 2. Compatible members become the generated class.
  final memberCount = _member.allMatches(out).length;
  if (memberCount > 0) {
    out = out.replaceAllMapped(_member, (m) => '$className.${m.group(1)}');
    changes.add(
        '$memberCount dotenv usage${memberCount == 1 ? '' : 's'} → $className');
  }

  // 3. What's left can't be translated automatically.
  for (final m in _anyDotenv.allMatches(out)) {
    final member = m.group(1)!;
    if (member == 'load') continue; // already reported by _removeLoads
    final hint =
        member == 'testLoad' ? ' (tests can read $className directly)' : '';
    notes.add(MigrationNote(lineOf(source.indexOf(m.group(0)!)),
        'dotenv.$member has no compiled equivalent; update by hand$hint.'));
  }
  for (final m in _dotEnvClass.allMatches(out)) {
    notes.add(MigrationNote(lineOf(m.start < source.length ? m.start : 0),
        'A DotEnv() instance is created here; switch it to $className by hand.'));
  }
  out = _removeUnusedClientEnvConstant(out, changes, notes, lineOf, source);

  if (!changes.isNotEmpty) return MigrationResult(source, changes, notes);

  // 4. Imports: add the generated file, drop flutter_dotenv when unused.
  if (!out.contains(importUri)) {
    out = _addImport(out, importUri);
    changes.add("import '$importUri'");
  }
  final withoutImports = out.replaceAll(_import, '');
  if (!RegExp(r'(?<![\w$])(dotenv|DotEnv)\b').hasMatch(withoutImports)) {
    final before = out;
    out = out.replaceAll(_import, '');
    if (out != before) changes.add('removed the flutter_dotenv import');
  }
  return MigrationResult(out, changes, notes);
}