Add generator file.

Expects a file to be placed in /third_party/preferred_extension.mime.types.
(Or pointed to on command line).
diff --git a/pkgs/mime/tool/generate_defaults.dart b/pkgs/mime/tool/generate_defaults.dart
new file mode 100644
index 0000000..4124432
--- /dev/null
+++ b/pkgs/mime/tool/generate_defaults.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+const startMarker = '\n  // GENERATED by tool/generate_defaults.dart\n';
+const endMarker = '\n  // END GENERATED\n';
+const androidMimeTypesPath = '../third_party/preferred_extension.mime.types';
+const extensionLibraryPath = '../lib/src/extension.dart';
+
+void main(List<String> args) {
+  var dryRun = false;
+  var printUsage = false;
+  var hadInvalidArgument = false;
+  Uri? mimeTypesOverride;
+  for (var arg in args) {
+    if (arg == '-h' || arg == '--help') {
+      printUsage = true;
+    } else if (arg == '-n' || arg == '--dryrun' || arg == '--dry-run') {
+      dryRun = true;
+    } else if (!arg.startsWith('-') && mimeTypesOverride == null) {
+      mimeTypesOverride = Directory.current.uri.resolve(arg);
+    } else {
+      hadInvalidArgument = true;
+      stderr.writeln('Unexpected command-line argument: $arg');
+    }
+  }
+  if (hadInvalidArgument) {
+    stderr.writeln(usage);
+    exit(1);
+  } else if (printUsage) {
+    stdout.writeln(usage);
+    exit(0);
+  }
+  final script = Platform.script;
+  final library = script.resolve(extensionLibraryPath);
+  final libraryFile = File.fromUri(library);
+  final librarySource = libraryFile.readAsStringSync();
+  final int startTagOffset, endTagOffset;
+  if (librarySource.indexOf(startMarker) case >= 0 && final start) {
+    startTagOffset = start;
+    if (librarySource.indexOf(endMarker, start + startMarker.length)
+        case >= 0 && final end) {
+      endTagOffset = end;
+    } else {
+      stderr.writeln('Library file (${libraryFile.path})\n'
+          ' does not contain end marker: $endMarker');
+      exit(1);
+    }
+  } else {
+    stderr.writeln('Library file (${libraryFile.path})\n'
+        ' does not contain start marker: $startMarker');
+    exit(1);
+  }
+
+  final androidDefaults =
+      mimeTypesOverride ?? script.resolve(androidMimeTypesPath);
+  final defaultsFile = File.fromUri(androidDefaults);
+  if (!defaultsFile.existsSync()) {
+    stderr.writeln('Cannot find file: ${androidDefaults.toFilePath()}');
+    if (mimeTypesOverride == null) {
+      stderr.writeln('Download file from rx: \n'
+          // ignore: missing_whitespace_between_adjacent_strings
+          '  https://android.googlesource.com/platform/frameworks/'
+          'base/+/main/mime/java-res/android.mime.types');
+    }
+    exit(1);
+  }
+  final defaults = defaultsFile.readAsLinesSync();
+  final mapping = <String, String>{};
+  for (var line in defaults) {
+    if (line.startsWith('#')) continue;
+    final parts = line.split(' ');
+    var mediaType = parts[0];
+    var defaultExtension = parts[1];
+    if (defaultExtension.startsWith('?')) {
+      defaultExtension = defaultExtension.substring(1);
+    }
+    if (mediaType.startsWith('?')) {
+      mediaType = mediaType.substring(1);
+      mapping[mediaType] ??= defaultExtension;
+    } else {
+      mapping[mediaType] = defaultExtension;
+    }
+  }
+  final sortedKeys = mapping.keys.toList()..sort();
+  final newMapping =
+      [for (var key in sortedKeys) "  '$key': '${mapping[key]}',"].join('\n');
+  final newLibrarySource = librarySource.replaceRange(
+      startTagOffset + startMarker.length, endTagOffset, newMapping);
+  if (newLibrarySource != librarySource) {
+    if (!dryRun) {
+      libraryFile.writeAsStringSync(newLibrarySource);
+      stdout.writeln('Changes to mapping written to file.');
+    } else {
+      stdout.writeln('Changes to mapping. (Not written as dry-run.)');
+    }
+  } else {
+    stdout.writeln('No change to mapping');
+  }
+}
+
+const String usage = '''
+Usage: dart tool/generate_defaults.dart [-h|-n] [MIMETYPES]
+
+--help or -h    : Print this usage information
+--dry-run or -n : Don't write changes back to library
+MIMETYPES       : Path to file in the `mime.types` format
+                  where the first extension for each media type
+                  is the preferred extension.
+''';