|  | #!/usr/bin/env dart | 
|  |  | 
|  | // Copyright (c) 2020, 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'; | 
|  |  | 
|  | final commentLine = RegExp(r'^///?'); | 
|  | final scriptLine = RegExp(r'^#\!'); | 
|  | final languageMarker = RegExp(r"^\s*//\s*@dart\s*="); | 
|  |  | 
|  | void main(List<String> args) { | 
|  | if (args.isEmpty) { | 
|  | print('Mark files passed on the command line or under a directory'); | 
|  | print(' passed on the command line as opted out of null safety.  Does'); | 
|  | print(' not mark files under directories containing a pubspec.yaml file'); | 
|  | print(' unless the file is specified explicitly in the argument list.'); | 
|  | print(' Ignores files not ending in ".dart".'); | 
|  | print('Usage: opt_files_out.dart <files or directories>'); | 
|  | return; | 
|  | } | 
|  | for (var name in args) { | 
|  | switch (FileSystemEntity.typeSync(name)) { | 
|  | case FileSystemEntityType.directory: | 
|  | markDirectory(Directory(name)); | 
|  | break; | 
|  | case FileSystemEntityType.file: | 
|  | markFile(File(name)); | 
|  | break; | 
|  | default: | 
|  | print("Ignoring unknown object $name"); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isPubSpec(FileSystemEntity entity) => | 
|  | entity is File && entity.path.endsWith("pubspec.yaml"); | 
|  |  | 
|  | void markEntity(FileSystemEntity entity) { | 
|  | if (entity is File) { | 
|  | markFile(entity); | 
|  | } else if (entity is Directory) { | 
|  | markDirectory(entity); | 
|  | } else { | 
|  | print("Ignoring unknown object ${entity.path}"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void markDirectory(Directory dir) { | 
|  | var children = dir.listSync(); | 
|  | if (children.any(isPubSpec)) { | 
|  | print("Skipping files under ${dir.path}"); | 
|  | return; | 
|  | } | 
|  | for (var child in children) { | 
|  | markEntity(child); | 
|  | } | 
|  | } | 
|  |  | 
|  | void markFile(File file) { | 
|  | if (!file.path.endsWith(".dart")) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | List<String> lines; | 
|  | try { | 
|  | lines = file.readAsLinesSync(); | 
|  | } catch (e) { | 
|  | print("Failed to read file ${file.path}: $e"); | 
|  | return; | 
|  | } | 
|  | if (lines.any((line) => line.startsWith(languageMarker))) { | 
|  | print("Skipping already marked file ${file.path}"); | 
|  | return; | 
|  | } | 
|  | var marked = markContents(lines); | 
|  | try { | 
|  | file.writeAsStringSync(marked); | 
|  | } catch (e) { | 
|  | print("Failed to write file ${file.path}: $e"); | 
|  | return; | 
|  | } | 
|  | print("Marked ${file.path}"); | 
|  | } | 
|  |  | 
|  | String markContents(List<String> lines) { | 
|  | var buffer = StringBuffer(); | 
|  | var marked = false; | 
|  |  | 
|  | for (var line in lines) { | 
|  | // If the file has not yet been marked, and we have reached the | 
|  | // first non-comment line, insert an opt out marker. | 
|  | if (!marked && | 
|  | (!commentLine.hasMatch(line)) && | 
|  | (!scriptLine.hasMatch(line))) { | 
|  | buffer.write('\n// @dart = 2.9\n'); | 
|  | marked = true; | 
|  | } | 
|  | buffer.write('$line\n'); | 
|  | } | 
|  |  | 
|  | // In case of empty file, or file of all comments (who does that!?). | 
|  | if (!marked) { | 
|  | buffer.write('\n// @dart = 2.9\n'); | 
|  | } | 
|  | return buffer.toString(); | 
|  | } |