From 5dbe97787a452829e7702222bcd2554d299c3707 Mon Sep 17 00:00:00 2001 From: nilsreichardt Date: Thu, 26 Feb 2026 17:57:18 +0100 Subject: [PATCH] Fix `flutterfire configure` command when existing `firebase_options.dart` has a wrong format --- .../firebase_dart_configuration_write.dart | 34 +++++++--- ...irebase_dart_configuration_write_test.dart | 62 +++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 packages/flutterfire_cli/test/firebase_dart_configuration_write_test.dart diff --git a/packages/flutterfire_cli/lib/src/firebase/firebase_dart_configuration_write.dart b/packages/flutterfire_cli/lib/src/firebase/firebase_dart_configuration_write.dart index 5dd4ed04..59e925c3 100644 --- a/packages/flutterfire_cli/lib/src/firebase/firebase_dart_configuration_write.dart +++ b/packages/flutterfire_cli/lib/src/firebase/firebase_dart_configuration_write.dart @@ -59,11 +59,8 @@ class FirebaseDartConfigurationWrite { ); outputFile.writeAsStringSync(updatedFileString); } else { - _writeHeader(); - _writeClass(); - outputFile.createSync(recursive: true); - outputFile.writeAsStringSync(_stringBuffer.toString()); + outputFile.writeAsStringSync(_buildConfigurationFile()); } return _firebaseJsonWrites(); @@ -130,14 +127,28 @@ class FirebaseDartConfigurationWrite { platform == kWeb ? 'if (kIsWeb)' : 'case TargetPlatform.$platform:', ), ); - final unsupportedErrorLineIndex = startIndex + 1; - if (fileConfigurationLines[unsupportedErrorLineIndex] - .contains('UnsupportedError')) { + // Existing file does not match generated FlutterFire structure. + // Regenerate so configure can recover from template placeholders. + if (startIndex == -1) { + return _buildConfigurationFile(); + } + + final unsupportedErrorLineIndex = fileConfigurationLines.indexWhere( + (line) => + line.contains('throw UnsupportedError(') || + line.contains('throw UnimplementedError('), + startIndex, + ); + + if (unsupportedErrorLineIndex != -1) { final endIndex = fileConfigurationLines.indexWhere( (line) => line.contains(');'), unsupportedErrorLineIndex, ); + if (endIndex == -1) { + return _buildConfigurationFile(); + } fileConfigurationLines.removeRange( unsupportedErrorLineIndex, endIndex + 1, @@ -150,7 +161,7 @@ class FirebaseDartConfigurationWrite { : ' return ${platform.toLowerCase()};', ); } else { - throw Exception('`UnsupportedError` not found in $platform'); + return _buildConfigurationFile(); } final insertIndex = fileConfigurationLines.lastIndexOf('}'); @@ -169,6 +180,13 @@ class FirebaseDartConfigurationWrite { return formatList(fileConfigurationLines).join('\n'); } + String _buildConfigurationFile() { + _stringBuffer.clear(); + _writeHeader(); + _writeClass(); + return _stringBuffer.toString(); + } + // ensure only one empty line between each static property List formatList(List items) { return items.fold>([], (acc, item) { diff --git a/packages/flutterfire_cli/test/firebase_dart_configuration_write_test.dart b/packages/flutterfire_cli/test/firebase_dart_configuration_write_test.dart new file mode 100644 index 00000000..88c42bc5 --- /dev/null +++ b/packages/flutterfire_cli/test/firebase_dart_configuration_write_test.dart @@ -0,0 +1,62 @@ +import 'dart:io'; + +import 'package:flutterfire_cli/src/firebase/firebase_dart_configuration_write.dart'; +import 'package:flutterfire_cli/src/firebase/firebase_options.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +void main() { + group('FirebaseDartConfigurationWrite', () { + // Regression test for + // https://github.com/invertase/flutterfire_cli/issues/422 + test( + 'replaces placeholder firebase_options.dart with generated output', + () { + final tempDir = Directory.systemTemp.createTempSync(); + addTearDown(() => tempDir.deleteSync(recursive: true)); + + final filePath = p.join(tempDir.path, 'lib', 'firebase_options.dart'); + File(filePath) + ..createSync(recursive: true) + ..writeAsStringSync(''' +// File normally generated by FlutterFire CLI. This is a stand-in. +// See README.md for details. +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; + +class DefaultFirebaseOptions { + // TODO: Remove this file and run `flutterfire config` for Firebase options + static FirebaseOptions get currentPlatform { + throw UnimplementedError( + 'Generate this file by running `flutterfire configure`. ' + 'See README.md for details.', + ); + } +} +'''); + + final writer = FirebaseDartConfigurationWrite( + configurationFilePath: filePath, + flutterAppPath: tempDir.path, + firebaseProjectId: 'test-project-id', + webOptions: const FirebaseOptions( + optionsSourceContent: '{}', + optionsSourceFileName: 'firebase-config.json', + apiKey: 'api-key', + appId: 'app-id', + messagingSenderId: 'sender-id', + projectId: 'test-project-id', + measurementId: 'measurement-id', + ), + ); + + writer.write(); + + final updatedContent = File(filePath).readAsStringSync(); + expect(updatedContent, contains('if (kIsWeb) {')); + expect(updatedContent, contains('return web;')); + expect(updatedContent, contains('static const FirebaseOptions web')); + expect(updatedContent, isNot(contains('UnimplementedError'))); + }, + ); + }); +}