blob: eef534363113b6e01aa6ed45566fdd16bfc31170 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. 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';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
final ArgParser argParser = ArgParser()
..addFlag('ui', defaultsTo: false)
..addFlag('engine', defaultsTo: false)
final List<Replacer> uiPatterns = <Replacer>[
AllReplacer(RegExp(r'library\s+ui;'), 'library dart.ui;'),
AllReplacer(RegExp(r'part\s+of\s+ui;'), 'part of dart.ui;'),
'''), r'''
import 'dart:_engine' as engine;
export 'dart:_engine'
final List<Replacer> engineLibraryPatterns = <Replacer>[
AllReplacer(RegExp(r'library\s+engine;'), 'library dart._engine;'),
import\s*'../ui.dart' as ui;
'''), r'''
import 'dart:ui' as ui;
import\s*'package:ui/ui.dart' as ui;
'''), r'''
import 'dart:ui' as ui;
// Remove imports of engine part files.
AllReplacer(RegExp(r"import\s*'engine/.*';"), ''),
// Replace exports of engine files with "part" directives.
'''), (Match match) => '''
part 'engine/${}';
r"import 'dart:_js_annotations'",
final List<Replacer> enginePartsPatterns = <Replacer>[
AllReplacer(RegExp(r'part\s+of\s+engine;'), 'part of dart._engine;'),
// Remove library-level JS annotations.
AllReplacer(RegExp(r'\n@JS(.*)\nlibrary .+;'), ''),
// Remove library directives.
AllReplacer(RegExp(r'\nlibrary .+;'), ''),
// Remove imports/exports from all engine parts.
AllReplacer(RegExp(r'\nimport\s*.*'), ''),
AllReplacer(RegExp(r'\nexport\s*.*'), ''),
final List<Replacer> sharedPatterns = <Replacer>[
AllReplacer(RegExp(r"import\s*'package:meta/meta.dart';"), ''),
AllReplacer('@required', ''),
AllReplacer('@protected', ''),
AllReplacer('@mustCallSuper', ''),
AllReplacer('@immutable', ''),
AllReplacer('@visibleForTesting', ''),
// Rewrites the "package"-style web ui library into a dart:ui implementation.
// So far this only requires a replace of the library declarations.
void main(List<String> arguments) {
final ArgResults results = argParser.parse(arguments);
final Directory directory = Directory(results['output-dir'] as String);
final String inputDirectoryPath = results['input-dir'] as String;
for (final String inputFilePath in results['input'] as Iterable<String>) {
final File inputFile = File(inputFilePath);
final File outputFile = File(path.join(
directory.path, inputFile.path.substring(inputDirectoryPath.length)))
..createSync(recursive: true);
final String source = inputFile.readAsStringSync();
final String rewrittenContent = rewriteFile(
filePath: inputFilePath,
isUi: results['ui'] as bool,
isEngine: results['engine'] as bool,
if (results['stamp'] != null) {
File(results['stamp'] as String).writeAsStringSync('stamp');
String rewriteFile(String source, {required String filePath, required bool isUi, required bool isEngine}) {
final List<Replacer> replacementPatterns = <Replacer>[];
if (isUi) {
} else if (isEngine) {
if (filePath.endsWith('lib/src/engine.dart')) {
} else {
source = _preprocessEnginePartFile(source);
for (final Replacer replacer in replacementPatterns) {
source = replacer.perform(source);
return source;
String _preprocessEnginePartFile(String source) {
if (source.startsWith('part of engine;') || source.contains('\npart of engine;')) {
// The file hasn't been migrated yet.
// Do nothing.
} else {
// Insert the part directive at the beginning of the file.
source = 'part of engine;\n' + source;
return source;
/// Responsible for performing string replacements.
abstract class Replacer {
/// Performs the replacement in the provided [text].
String perform(String text);
/// Replaces all occurrences of a pattern with a fixed string.
class AllReplacer implements Replacer {
/// Creates a new tuple with the given [pattern] and [replacement] string.
AllReplacer(this._pattern, this._replacement);
/// The pattern to be replaced.
final Pattern _pattern;
/// The replacement string.
final String _replacement;
String perform(String text) {
return text.replaceAll(_pattern, _replacement);
/// Uses a callback to replace each occurrence of a pattern.
class MappedReplacer implements Replacer {
MappedReplacer(this._pattern, this._replace);
/// The pattern to be replaced.
final RegExp _pattern;
/// A callback to replace each occurrence of [_pattern].
final String Function(Match match) _replace;
String perform(String text) {
return text.replaceAllMapped(_pattern, _replace);