From cfa3ca1c79d41ccc2f04b7f0a4198f8dbc66264b Mon Sep 17 00:00:00 2001 From: vonarian Date: Sun, 5 Oct 2025 10:05:32 +0330 Subject: [PATCH 1/5] Update Dart SDK constraint to >=3.9.2 <4.0.0 --- puro/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puro/pubspec.yaml b/puro/pubspec.yaml index 71491c0..f3ee841 100644 --- a/puro/pubspec.yaml +++ b/puro/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/pingbird/puro homepage: https://puro.dev environment: - sdk: '>=3.1.3 <4.0.0' + sdk: '>=3.9.2 <4.0.0' dependencies: analyzer: ^7.4.5 From 0e5dde105e992575507f97d729759784d667c513 Mon Sep 17 00:00:00 2001 From: vonarian Date: Sun, 5 Oct 2025 10:07:52 +0330 Subject: [PATCH 2/5] - Replace deprecated whereNotNull() with nonNulls getter - Fix null-safety lint --- puro/lib/src/cli.dart | 2 + puro/lib/src/commands/prepare.dart | 81 ++++++++++++++++++++++++++++++ puro/lib/src/env/prepare.dart | 75 +++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 puro/lib/src/commands/prepare.dart create mode 100644 puro/lib/src/env/prepare.dart diff --git a/puro/lib/src/cli.dart b/puro/lib/src/cli.dart index 4d92e36..ea28614 100644 --- a/puro/lib/src/cli.dart +++ b/puro/lib/src/cli.dart @@ -25,6 +25,7 @@ import 'commands/pub.dart'; import 'commands/puro_install.dart'; import 'commands/puro_uninstall.dart'; import 'commands/puro_upgrade.dart'; +import 'commands/prepare.dart'; import 'commands/repl.dart'; import 'commands/run.dart'; import 'commands/version.dart'; @@ -268,6 +269,7 @@ void main(List args) async { ..addCommand(CleanCommand()) ..addCommand(EnvRmCommand()) ..addCommand(EnvRenameCommand()) + ..addCommand(PrepareCommand()) ..addCommand(FlutterCommand()) ..addCommand(DartCommand()) ..addCommand(PubCommand()) diff --git a/puro/lib/src/commands/prepare.dart b/puro/lib/src/commands/prepare.dart new file mode 100644 index 0000000..29d98f4 --- /dev/null +++ b/puro/lib/src/commands/prepare.dart @@ -0,0 +1,81 @@ +import '../command.dart'; +import '../command_result.dart'; +import '../env/default.dart'; +import '../env/prepare.dart'; +import '../logger.dart'; + +class PrepareCommand extends PuroCommand { + PrepareCommand() { + argParser + ..addFlag( + 'force', + help: 'Force re-downloading artifacts even if they already exist.', + negatable: false, + ) + ..addFlag( + 'all-platforms', + help: 'Precache artifacts for every supported Flutter platform.', + negatable: false, + ) + ..addMultiOption( + 'platform', + help: 'Precache artifacts for the provided platforms (android, ios, etc).', + valueHelp: 'name', + allowed: preparePlatformOptions.toList()..sort(), + ); + } + + @override + final name = 'prepare'; + + @override + final description = + 'Pre-downloads Flutter artifacts for an environment so builds can start immediately.'; + + @override + String? get argumentUsage => '[env]'; + + @override + Future run() async { + final log = PuroLogger.of(scope); + final envName = unwrapSingleOptionalArgument(); + final environment = await getProjectEnvOrDefault( + scope: scope, + envName: envName, + ); + + final force = argResults!['force'] as bool; + final allPlatforms = argResults!['all-platforms'] as bool; + final requestedPlatforms = + (argResults!['platform'] as List).map((e) => e.toLowerCase()); + final sortedRequested = sortPreparePlatforms(requestedPlatforms); + + final platforms = sortedRequested.isNotEmpty + ? sortedRequested + : (allPlatforms ? [] : defaultPreparePlatforms()); + + log.d( + 'Preparing environment `${environment.name}` for platforms: ' + '${allPlatforms ? 'all platforms' : (platforms.isEmpty ? 'default set' : platforms.join(', '))}' + '${force ? ' (force)' : ''}', + ); + + await prepareEnvironment( + scope: scope, + environment: environment, + platforms: platforms, + allPlatforms: allPlatforms, + force: force, + ); + + final platformSummary = allPlatforms + ? 'all platforms' + : (platforms.isEmpty + ? 'default platforms (${defaultPreparePlatforms().join(', ')})' + : platforms.join(', ')); + + return BasicMessageResult( + 'Prepared environment `${environment.name}` (${platformSummary}${force ? ', forced' : ''})', + ); + } +} diff --git a/puro/lib/src/env/prepare.dart b/puro/lib/src/env/prepare.dart new file mode 100644 index 0000000..7b3ca2c --- /dev/null +++ b/puro/lib/src/env/prepare.dart @@ -0,0 +1,75 @@ +import 'dart:io'; + +import '../command_result.dart'; +import '../config.dart'; +import '../provider.dart'; +import 'command.dart'; + +const Set preparePlatformOptions = { + 'android', + 'ios', + 'linux', + 'macos', + 'windows', + 'web', + 'fuchsia', +}; + +const List _platformOrder = [ + 'android', + 'ios', + 'macos', + 'linux', + 'windows', + 'web', + 'fuchsia', +]; + +List sortPreparePlatforms(Iterable platforms) { + final platformSet = platforms.toSet(); + return _platformOrder.where(platformSet.contains).toList(); +} + +List defaultPreparePlatforms() { + final platforms = {'android', 'web'}; + if (Platform.isMacOS) { + platforms..add('ios')..add('macos'); + } + if (Platform.isLinux) { + platforms.add('linux'); + } + if (Platform.isWindows) { + platforms.add('windows'); + } + return sortPreparePlatforms(platforms); +} + +Future prepareEnvironment({ + required Scope scope, + required EnvConfig environment, + List? platforms, + bool allPlatforms = false, + bool force = false, +}) async { + final args = ['precache']; + if (force) { + args.add('--force'); + } + if (allPlatforms) { + args.add('--all-platforms'); + } + for (final platform in platforms ?? const []) { + args.add('--$platform'); + } + final exitCode = await runFlutterCommand( + scope: scope, + environment: environment, + args: args, + mode: ProcessStartMode.inheritStdio, + ); + if (exitCode != 0) { + throw CommandError( + '`flutter ${args.join(' ')}` failed with exit code $exitCode', + ); + } +} From 175a07d578794cfffa2635e1485a0acb8809426f Mon Sep 17 00:00:00 2001 From: vonarian Date: Sun, 5 Oct 2025 10:16:49 +0330 Subject: [PATCH 3/5] Update CHANGELOG and add `prepare` command for pre-downloading Flutter artifacts --- puro/CHANGELOG.md | 4 + puro/lib/src/ast/binary.dart | 50 +- puro/lib/src/ast/grammar.dart | 302 ++++++------ puro/lib/src/ast/reader.dart | 10 +- puro/lib/src/cli.dart | 27 +- puro/lib/src/command.dart | 44 +- puro/lib/src/command_result.dart | 114 +++-- puro/lib/src/commands/build_shell.dart | 5 +- puro/lib/src/commands/env_rename.dart | 6 +- puro/lib/src/commands/env_use.dart | 14 +- puro/lib/src/commands/eval.dart | 11 +- puro/lib/src/commands/flutter.dart | 5 +- .../internal_generate_ast_parser.dart | 136 +++--- .../src/commands/internal_generate_docs.dart | 32 +- puro/lib/src/commands/ls_versions.dart | 110 ++--- puro/lib/src/commands/prepare.dart | 15 +- puro/lib/src/commands/puro_install.dart | 38 +- puro/lib/src/commands/puro_uninstall.dart | 12 +- puro/lib/src/commands/puro_upgrade.dart | 33 +- puro/lib/src/commands/repl.dart | 34 +- puro/lib/src/commands/version.dart | 11 +- puro/lib/src/config.dart | 285 ++++++------ puro/lib/src/engine/build_env.dart | 26 +- puro/lib/src/engine/dart_version.dart | 5 +- puro/lib/src/engine/depot_tools.dart | 4 +- puro/lib/src/engine/prepare.dart | 41 +- puro/lib/src/engine/visual_studio.dart | 78 ++-- puro/lib/src/engine/worker.dart | 80 ++-- puro/lib/src/env/create.dart | 60 +-- puro/lib/src/env/dart.dart | 96 ++-- puro/lib/src/env/default.dart | 9 +- puro/lib/src/env/delete.dart | 50 +- puro/lib/src/env/engine.dart | 131 ++---- puro/lib/src/env/env_shims.dart | 26 +- puro/lib/src/env/flutter_tool.dart | 33 +- puro/lib/src/env/gc.dart | 11 +- puro/lib/src/env/list.dart | 151 +++--- puro/lib/src/env/prepare.dart | 9 +- puro/lib/src/env/releases.dart | 29 +- puro/lib/src/env/rename.dart | 18 +- puro/lib/src/env/upgrade.dart | 28 +- puro/lib/src/env/version.dart | 25 +- puro/lib/src/eval/context.dart | 75 +-- puro/lib/src/eval/packages.dart | 15 +- puro/lib/src/eval/parse.dart | 47 +- puro/lib/src/eval/worker.dart | 23 +- puro/lib/src/extensions.dart | 91 +--- puro/lib/src/file_lock.dart | 40 +- puro/lib/src/git.dart | 433 +++++++----------- puro/lib/src/http.dart | 22 +- puro/lib/src/install/bin.dart | 48 +- puro/lib/src/install/profile.dart | 136 +++--- puro/lib/src/install/upgrade.dart | 29 +- puro/lib/src/json_edit/editor.dart | 54 +-- puro/lib/src/json_edit/element.dart | 20 +- puro/lib/src/json_edit/grammar.dart | 143 +++--- puro/lib/src/logger.dart | 39 +- puro/lib/src/process.dart | 85 ++-- puro/lib/src/progress.dart | 19 +- puro/lib/src/provider.dart | 4 +- puro/lib/src/terminal.dart | 13 +- puro/lib/src/version.dart | 54 +-- puro/lib/src/workspace/clean.dart | 60 +-- puro/lib/src/workspace/gitignore.dart | 23 +- puro/lib/src/workspace/install.dart | 80 ++-- puro/lib/src/workspace/intellij.dart | 121 ++--- puro/lib/src/workspace/vscode.dart | 50 +- puro/test/json_edit_test.dart | 268 +++-------- website/docs/reference/manual.md | 15 + 69 files changed, 1814 insertions(+), 2401 deletions(-) diff --git a/puro/CHANGELOG.md b/puro/CHANGELOG.md index 311323c..8d47992 100644 --- a/puro/CHANGELOG.md +++ b/puro/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +* Added the `prepare` command to pre-download Flutter artifacts for an environment + ## 1.4.11 * Bug fixes diff --git a/puro/lib/src/ast/binary.dart b/puro/lib/src/ast/binary.dart index 87ea2d1..233531f 100644 --- a/puro/lib/src/ast/binary.dart +++ b/puro/lib/src/ast/binary.dart @@ -322,53 +322,33 @@ abstract class BinType { } return NamedBinType(schema); } else if (schema['option'] != null) { - return OptionBinType(BinType.fromSchema( - schema['option'], - typeSchemas, - enumNames, - )); + return OptionBinType( + BinType.fromSchema(schema['option'], typeSchemas, enumNames), + ); } else if (schema['list'] != null) { - return ListBinType(BinType.fromSchema( - schema['list'], - typeSchemas, - enumNames, - )); + return ListBinType( + BinType.fromSchema(schema['list'], typeSchemas, enumNames), + ); } else if (schema['rlist'] != null) { - return RListBinType(BinType.fromSchema( - schema['rlist'], - typeSchemas, - enumNames, - )); + return RListBinType( + BinType.fromSchema(schema['rlist'], typeSchemas, enumNames), + ); } else if (schema['pair'] != null) { return PairBinType( - BinType.fromSchema( - schema['pair'][0], - typeSchemas, - enumNames, - ), - BinType.fromSchema( - schema['pair'][1], - typeSchemas, - enumNames, - ), + BinType.fromSchema(schema['pair'][0], typeSchemas, enumNames), + BinType.fromSchema(schema['pair'][1], typeSchemas, enumNames), ); } else if (schema['array'] != null) { return ArrayBinType( schema['array'][0] as String, - BinType.fromSchema( - schema['array'][1], - typeSchemas, - enumNames, - ), + BinType.fromSchema(schema['array'][1], typeSchemas, enumNames), ); } else if (schema['union'] != null) { return NamedBinType(typeSchemas[schema['union'][0]]![1] as String); } else if (schema['ifPrivate'] != null) { - return IfPrivateBinType(BinType.fromSchema( - schema['ifPrivate'], - typeSchemas, - enumNames, - )); + return IfPrivateBinType( + BinType.fromSchema(schema['ifPrivate'], typeSchemas, enumNames), + ); } else { throw UnimplementedError('Unknown type: $schema'); } diff --git a/puro/lib/src/ast/grammar.dart b/puro/lib/src/ast/grammar.dart index 535453a..c06e04f 100644 --- a/puro/lib/src/ast/grammar.dart +++ b/puro/lib/src/ast/grammar.dart @@ -1,4 +1,3 @@ -import 'package:collection/collection.dart'; import 'package:petitparser/petitparser.dart'; class BinaryMdGrammar extends GrammarDefinition { @@ -6,18 +5,14 @@ class BinaryMdGrammar extends GrammarDefinition { Parser start() => ref0(topLevelDecls).end(); Parser topLevelDecls() => - ref0(topLevelDecl).star().map((e) => e.whereNotNull().toList()); + ref0(topLevelDecl).star().map((e) => e.nonNulls.toList()); final ignoreDecls = { ['Byte flags (flag1, flag2, ..., flagN)'], ['Byte byte (10xxxxxx)'], ['type Byte = a byte'], ['abstract type UInt {}'], - [ - 'type UInt7 extends UInt {', - ' Byte byte1(0xxxxxxx);', - '}', - ], + ['type UInt7 extends UInt {', ' Byte byte1(0xxxxxxx);', '}'], [ 'type UInt14 extends UInt {', ' Byte byte1(10xxxxxx);', @@ -32,40 +27,17 @@ class BinaryMdGrammar extends GrammarDefinition { ' Byte byte4(xxxxxxxx);', '}', ], - [ - 'type List {', - ' UInt length;', - ' T[length] items;', - '}', - ], + ['type List {', ' UInt length;', ' T[length] items;', '}'], [ 'type RList {', ' T[length] elements;', ' Uint32 length;', // lol '}', ], - [ - 'type RList {', - ' T[length] elements;', - ' UInt32 length;', - '}', - ], - [ - 'type Pair {', - ' T0 first;', - ' T1 second;', - '}', - ], - [ - 'type Option {', - ' Byte tag;', - '}', - ], - [ - 'type Nothing extends Option {', - ' Byte tag = 0;', - '}', - ], + ['type RList {', ' T[length] elements;', ' UInt32 length;', '}'], + ['type Pair {', ' T0 first;', ' T1 second;', '}'], + ['type Option {', ' Byte tag;', '}'], + ['type Nothing extends Option {', ' Byte tag = 0;', '}'], [ 'type Something extends Option {', ' Byte tag = 1;', @@ -77,145 +49,157 @@ class BinaryMdGrammar extends GrammarDefinition { }; Parser topLevelDecl() => [ - for (final ignoreDecl in ignoreDecls) - [for (final line in ignoreDecl) string(line.trim()).trim(space())] - .toSequenceParser() - .map((e) => null), - ref0(typeDecl), - ref0(enumDecl), - failure('Expected top-level declaration'), - ].map((e) => e.trim(space())).toChoiceParser(); + for (final ignoreDecl in ignoreDecls) + [ + for (final line in ignoreDecl) string(line.trim()).trim(space()), + ].toSequenceParser().map((e) => null), + ref0(typeDecl), + ref0(enumDecl), + failure('Expected top-level declaration'), + ].map((e) => e.trim(space())).toChoiceParser(); - Parser typeDecl() => (string('abstract ').optional() & - string('type') & - ref0(identifier).trim(space()) & - (string('extends') & ref0(identifier).trim(space())) - .pick(1) - .optional() & - string('{') & - ref0(fieldDecl).star() & - string('}')) - .map((e) => { - 'type': [e[0] != null, e[2], e[3], e[5]] - }); + Parser typeDecl() => + (string('abstract ').optional() & + string('type') & + ref0(identifier).trim(space()) & + (string('extends') & ref0(identifier).trim(space())) + .pick(1) + .optional() & + string('{') & + ref0(fieldDecl).star() & + string('}')) + .map( + (e) => { + 'type': [e[0] != null, e[2], e[3], e[5]], + }, + ); - Parser enumDecl() => (string('enum') & - ref0(identifier) & - string('{') & - ((ref0(identifier) & - string('=').trim(space()) & - digit().star().flatten().trim(space()) & - string(',').trim(space()).optional()) - .map((e) => [e[0], e[2]]) | - (ref0(identifier) & string(',').trim(space()).optional()) - .map((e) => [e[0], null])) - .star() & - string('}')) - .map((e) => { - 'enum': [e[1], e[3]] - }); + Parser enumDecl() => + (string('enum') & + ref0(identifier) & + string('{') & + ((ref0(identifier) & + string('=').trim(space()) & + digit().star().flatten().trim(space()) & + string(',').trim(space()).optional()) + .map((e) => [e[0], e[2]]) | + (ref0(identifier) & string(',').trim(space()).optional()) + .map((e) => [e[0], null])) + .star() & + string('}')) + .map( + (e) => { + 'enum': [e[1], e[3]], + }, + ); Parser tpeInner() => [ - string('Uint').map((e) => 'UInt'), // lol - string('UInt30').map((e) => 'UInt'), // lol - (string('List<') & ref0(tpe) & string('>')) - .pick(1) - .map((e) => {'list': e}), - (string('RList<') & ref0(tpe) & string('>')) - .pick(1) - .map((e) => {'rlist': e}), - (string('Option<') & ref0(tpe) & string('>')) - .pick(1) - .map((e) => {'option': e}), - (string('Pair<') & - ref0(tpe) & - string(',').trim(space()) & - ref0(tpe) & - string('>')) - .map((e) => { - 'pair': [e[1], e[3]] - }), - (string('[') & ref0(tpe) & string(',') & ref0(tpe) & string(']')) - .map((e) => { - 'pair': [e[1], e[3]] - }), - identifier(), - ].toChoiceParser().trim(space()); + string('Uint').map((e) => 'UInt'), // lol + string('UInt30').map((e) => 'UInt'), // lol + (string('List<') & ref0(tpe) & string('>')).pick(1).map((e) => {'list': e}), + (string('RList<') & ref0(tpe) & string('>')) + .pick(1) + .map((e) => {'rlist': e}), + (string('Option<') & ref0(tpe) & string('>')) + .pick(1) + .map((e) => {'option': e}), + (string('Pair<') & + ref0(tpe) & + string(',').trim(space()) & + ref0(tpe) & + string('>')) + .map( + (e) => { + 'pair': [e[1], e[3]], + }, + ), + (string('[') & ref0(tpe) & string(',') & ref0(tpe) & string(']')).map( + (e) => { + 'pair': [e[1], e[3]], + }, + ), + identifier(), + ].toChoiceParser().trim(space()); - Parser tpeArr() => (ref0(tpeInner) & - (string('[') & any().starLazy(string(']')).flatten() & string(']')) - .optional()) - .map((e) => e[1] != null - ? { - 'array': [e[0], e[1][1]] - } - : e[0]) - .trim(space()); + Parser tpeArr() => + (ref0(tpeInner) & + (string('[') & + any().starLazy(string(']')).flatten() & + string(']')) + .optional()) + .map( + (e) => e[1] != null + ? { + 'array': [e[0], e[1][1]], + } + : e[0], + ) + .trim(space()); Parser tpe() => (ref0(tpeArr) & (string('|') & ref0(tpe)).pick(1).optional()).map( (e) => e[1] != null ? { - 'union': [e[0], e[1]] + 'union': [e[0], e[1]], } : e[0], ); Parser fieldDecl() => [ - (string('Byte') & - ref0(identifier) & - ((string('; // Index into') & - string(' the').optional() & - ref0(identifier) & - string(' above.')) - .pick(2) | - (string('; // Index into') & - string(' the').optional() & - ref0(identifier) & - string('enum above.')) - .pick(2))) - .map((e) => { - 'field': [ - e[2], - e[1], - null, - ] - }), - (ref0(tpe) & - (ref0(space).optional() & ref0(identifier)).pick(1) & - ((string('=') & space() & any().plusLazy(string(';')).flatten()) - .pick(2) - .optional() & - string(';')) - .pick(0)) - .map((e) => { - 'field': [e[0], e[1], e[2]] - }), - (string('if name begins with \'_\' {') & ref0(fieldDecl) & string('}')) - .pick(1) - .map((e) => { - 'field': [ - {'ifPrivate': e['field'][0]}, - e['field'][1], - e['field'][2], - ] - }), - (ref0(tpe) & - ref0(identifier) & - pattern('{(') & - (ref0(identifier) & string(',').trim(ref0(space)).optional()) - .pick(0) - .star() & - pattern('})') & - string(';')) - .map((e) => { - 'bitfield': [ - e[0], - e[1], - e[3], - ] - }), - ].toChoiceParser(failureJoiner: selectFarthest).trim(ref0(space)); + (string('Byte') & + ref0(identifier) & + ((string('; // Index into') & + string(' the').optional() & + ref0(identifier) & + string(' above.')) + .pick(2) | + (string('; // Index into') & + string(' the').optional() & + ref0(identifier) & + string('enum above.')) + .pick(2))) + .map( + (e) => { + 'field': [e[2], e[1], null], + }, + ), + (ref0(tpe) & + (ref0(space).optional() & ref0(identifier)).pick(1) & + ((string('=') & space() & any().plusLazy(string(';')).flatten()) + .pick(2) + .optional() & + string(';')) + .pick(0)) + .map( + (e) => { + 'field': [e[0], e[1], e[2]], + }, + ), + (string('if name begins with \'_\' {') & ref0(fieldDecl) & string('}')) + .pick(1) + .map( + (e) => { + 'field': [ + {'ifPrivate': e['field'][0]}, + e['field'][1], + e['field'][2], + ], + }, + ), + (ref0(tpe) & + ref0(identifier) & + pattern('{(') & + (ref0(identifier) & string(',').trim(ref0(space)).optional()) + .pick(0) + .star() & + pattern('})') & + string(';')) + .map( + (e) => { + 'bitfield': [e[0], e[1], e[3]], + }, + ), + ].toChoiceParser(failureJoiner: selectFarthest).trim(ref0(space)); Parser lineComment() { return (string('//') & Token.newlineParser().neg().star()).flatten(); diff --git a/puro/lib/src/ast/reader.dart b/puro/lib/src/ast/reader.dart index 58cb8c8..a018038 100644 --- a/puro/lib/src/ast/reader.dart +++ b/puro/lib/src/ast/reader.dart @@ -6,8 +6,10 @@ class Reader { Reader(this._bytes); final Uint8List _bytes; - late final byteData = - _bytes.buffer.asByteData(_bytes.offsetInBytes, _bytes.lengthInBytes); + late final byteData = _bytes.buffer.asByteData( + _bytes.offsetInBytes, + _bytes.lengthInBytes, + ); var byteOffset = 0; int readByte() => _bytes[byteOffset++]; int peekByte() => _bytes[byteOffset]; @@ -50,8 +52,8 @@ class Reader { Uint8List? _doubleBufferUint8; double readDouble() { - final doubleBufferUint8 = - _doubleBufferUint8 ??= _doubleBuffer.buffer.asUint8List(); + final doubleBufferUint8 = _doubleBufferUint8 ??= _doubleBuffer.buffer + .asUint8List(); doubleBufferUint8[0] = readByte(); doubleBufferUint8[1] = readByte(); doubleBufferUint8[2] = readByte(); diff --git a/puro/lib/src/cli.dart b/puro/lib/src/cli.dart index ea28614..de7ec1e 100644 --- a/puro/lib/src/cli.dart +++ b/puro/lib/src/cli.dart @@ -51,15 +51,9 @@ void main(List args) async { final PuroLogger log; if (isJson) { - log = PuroLogger( - terminal: terminal, - onAdd: runner.logEntries.add, - ); + log = PuroLogger(terminal: terminal, onAdd: runner.logEntries.add); } else { - log = PuroLogger( - terminal: terminal, - level: LogLevel.warning, - ); + log = PuroLogger(terminal: terminal, level: LogLevel.warning); if (Platform.environment.containsKey('PURO_LOG_LEVEL')) { final logLevel = int.tryParse(Platform.environment['PURO_LOG_LEVEL']!); if (logLevel != null) { @@ -172,7 +166,8 @@ void main(List args) async { ) ..addOption( 'log-level', - help: 'Changes how much information is logged to the console, 0 being ' + help: + 'Changes how much information is logged to the console, 0 being ' 'no logging at all, and 4 being extremely verbose', valueHelp: '0-4', callback: runner.wrapCallback((str) { @@ -224,11 +219,7 @@ void main(List args) async { } }), ) - ..addFlag( - 'json', - help: 'Output in JSON where possible', - negatable: false, - ) + ..addFlag('json', help: 'Output in JSON where possible', negatable: false) ..addFlag( 'install', help: 'Whether to attempt to install puro', @@ -305,10 +296,8 @@ void main(List args) async { ), ); } catch (exception, stackTrace) { - await runner.writeResultAndExit(CommandErrorResult( - exception, - stackTrace, - log.level?.index ?? 0, - )); + await runner.writeResultAndExit( + CommandErrorResult(exception, stackTrace, log.level?.index ?? 0), + ); } } diff --git a/puro/lib/src/command.dart b/puro/lib/src/command.dart index b13fbc2..18bd25f 100644 --- a/puro/lib/src/command.dart +++ b/puro/lib/src/command.dart @@ -225,25 +225,25 @@ class PuroCommandRunner extends CommandRunner { LogLevel level = LogLevel.verbose, }) { backgroundTasks[() async { - try { - await task(); - } catch (exception, stackTrace) { - log.add(LogEntry( - clock.now(), - level, - 'Exception while $name\n$exception\n$stackTrace', - )); - } - }()] = name; + try { + await task(); + } catch (exception, stackTrace) { + log.add( + LogEntry( + clock.now(), + level, + 'Exception while $name\n$exception\n$stackTrace', + ), + ); + } + }()] = + name; } @override Future printUsage() async { await writeResultAndExit( - CommandHelpResult( - didRequestHelp: didRequestHelp, - usage: usage, - ), + CommandHelpResult(didRequestHelp: didRequestHelp, usage: usage), ); } @@ -319,10 +319,15 @@ class PuroCommandRunner extends CommandRunner { // Initialize config - final homeDir = - PuroConfig.getHomeDir(scope: scope, fileSystem: fileSystem); + final homeDir = PuroConfig.getHomeDir( + scope: scope, + fileSystem: fileSystem, + ); final puroRoot = PuroConfig.getPuroRoot( - scope: scope, fileSystem: fileSystem, homeDir: homeDir); + scope: scope, + fileSystem: fileSystem, + homeDir: homeDir, + ); final prefsJson = puroRoot.childFile('prefs.json'); scope.add(globalPrefsJsonFileProvider, prefsJson); final firstRun = @@ -351,10 +356,7 @@ class PuroCommandRunner extends CommandRunner { shouldSkipCacheSync: shouldSkipCacheSyncOverride, firstRun: firstRun, ); - scope.add( - PuroConfig.provider, - config, - ); + scope.add(PuroConfig.provider, config); scope.add(CommandMessage.provider, messages.add); final commandName = topLevelResults.command?.name; diff --git a/puro/lib/src/command_result.dart b/puro/lib/src/command_result.dart index 9f78ed5..6d377b8 100644 --- a/puro/lib/src/command_result.dart +++ b/puro/lib/src/command_result.dart @@ -8,9 +8,10 @@ extension CommandResultModelExtensions on CommandResultModel { void addMessage(CommandMessage message, OutputFormatter format) { messages.add( CommandMessageModel( - type: (message.type ?? - (success ? CompletionType.success : CompletionType.failure)) - .name, + type: + (message.type ?? + (success ? CompletionType.success : CompletionType.failure)) + .name, message: message.message(format), ), ); @@ -34,11 +35,13 @@ class CommandErrorResult extends CommandResult { Iterable get messages { return [ CommandMessage('$exception\n$stackTrace'), - CommandMessage([ - 'Puro crashed! Please file an issue at https://github.com/pingbird/puro', - if (logLevel != null && logLevel! < 4) - 'Consider running the command with a higher log level: `--log-level=4`', - ].join('\n')), + CommandMessage( + [ + 'Puro crashed! Please file an issue at https://github.com/pingbird/puro', + if (logLevel != null && logLevel! < 4) + 'Consider running the command with a higher log level: `--log-level=4`', + ].join('\n'), + ), ]; } @@ -47,20 +50,16 @@ class CommandErrorResult extends CommandResult { @override CommandResultModel? get model => CommandResultModel( - error: CommandErrorModel( - exception: '$exception', - exceptionType: '${exception.runtimeType}', - stackTrace: '$stackTrace', - ), - ); + error: CommandErrorModel( + exception: '$exception', + exceptionType: '${exception.runtimeType}', + stackTrace: '$stackTrace', + ), + ); } class CommandHelpResult extends CommandResult { - CommandHelpResult({ - required this.didRequestHelp, - this.help, - this.usage, - }); + CommandHelpResult({required this.didRequestHelp, this.help, this.usage}); final bool didRequestHelp; final String? help; @@ -68,19 +67,15 @@ class CommandHelpResult extends CommandResult { @override Iterable get messages => [ - if (message != null) - CommandMessage( - help!, - type: CompletionType.failure, - ), - if (usage != null) - CommandMessage( - usage!, - type: message == null && didRequestHelp - ? CompletionType.plain - : CompletionType.info, - ), - ]; + if (message != null) CommandMessage(help!, type: CompletionType.failure), + if (usage != null) + CommandMessage( + usage!, + type: message == null && didRequestHelp + ? CompletionType.plain + : CompletionType.info, + ), + ]; @override bool get success => didRequestHelp; @@ -104,11 +99,7 @@ class BasicMessageResult extends CommandResult { this.model, }) : messages = [CommandMessage.format(message, type: type)]; - BasicMessageResult.list( - this.messages, { - this.success = true, - this.model, - }); + BasicMessageResult.list(this.messages, {this.success = true, this.model}); @override final bool success; @@ -139,10 +130,10 @@ abstract class CommandResult { @override String toString() => CommandMessage.formatMessages( - messages: messages, - format: plainFormatter, - success: toModel().success, - ); + messages: messages, + format: plainFormatter, + success: toModel().success, + ); } class CommandMessage { @@ -158,11 +149,14 @@ class CommandMessage { required bool success, }) { return messages - .map((e) => format.complete( - e.message(format), - type: e.type ?? - (success ? CompletionType.success : CompletionType.failure), - )) + .map( + (e) => format.complete( + e.message(format), + type: + e.type ?? + (success ? CompletionType.success : CompletionType.failure), + ), + ) .join('\n'); } @@ -181,11 +175,11 @@ class CommandError implements Exception { CommandResultModel? model, bool success = false, }) : result = BasicMessageResult( - message, - success: success, - type: type, - model: model, - ); + message, + success: success, + type: type, + model: model, + ); CommandError.format( String Function(OutputFormatter format) message, { @@ -193,21 +187,21 @@ class CommandError implements Exception { CommandResultModel? model, bool success = false, }) : result = BasicMessageResult.format( - message, - success: success, - type: type, - model: model, - ); + message, + success: success, + type: type, + model: model, + ); CommandError.list( List messages, { CommandResultModel? model, bool success = false, }) : result = BasicMessageResult.list( - messages, - success: success, - model: model, - ); + messages, + success: success, + model: model, + ); final CommandResult result; diff --git a/puro/lib/src/commands/build_shell.dart b/puro/lib/src/commands/build_shell.dart index ccae072..ca2adac 100644 --- a/puro/lib/src/commands/build_shell.dart +++ b/puro/lib/src/commands/build_shell.dart @@ -23,10 +23,7 @@ class BuildShellCommand extends PuroCommand { await prepareEngineSystemDeps(scope: scope); - final exitCode = await runBuildEnvShell( - scope: scope, - command: command, - ); + final exitCode = await runBuildEnvShell(scope: scope, command: command); await runner.exitPuro(exitCode); } diff --git a/puro/lib/src/commands/env_rename.dart b/puro/lib/src/commands/env_rename.dart index dfa928d..f0ac1d2 100644 --- a/puro/lib/src/commands/env_rename.dart +++ b/puro/lib/src/commands/env_rename.dart @@ -17,11 +17,7 @@ class EnvRenameCommand extends PuroCommand { final args = unwrapArguments(exactly: 2); final name = args[0]; final newName = args[1]; - await renameEnvironment( - scope: scope, - name: name, - newName: newName, - ); + await renameEnvironment(scope: scope, name: name, newName: newName); return BasicMessageResult('Renamed environment `$name` to `$newName`'); } } diff --git a/puro/lib/src/commands/env_use.dart b/puro/lib/src/commands/env_use.dart index b77a39d..5f6b9a5 100644 --- a/puro/lib/src/commands/env_use.dart +++ b/puro/lib/src/commands/env_use.dart @@ -66,16 +66,14 @@ class EnvUseCommand extends PuroCommand { log.w('Environment `${env.name}` does not exist'); } } - await setDefaultEnvName( - scope: scope, - envName: env.name, - ); + await setDefaultEnvName(scope: scope, envName: env.name); return BasicMessageResult( 'Set global default environment to `${env.name}`', ); } - var vscodeOverride = - argResults!.wasParsed('vscode') ? argResults!['vscode'] as bool : null; + var vscodeOverride = argResults!.wasParsed('vscode') + ? argResults!['vscode'] as bool + : null; if (vscodeOverride == null && await isRunningInVscode(scope: scope)) { vscodeOverride = true; } @@ -88,8 +86,6 @@ class EnvUseCommand extends PuroCommand { : null, projectConfig: config.project, ); - return BasicMessageResult( - 'Switched to environment `${environment.name}`', - ); + return BasicMessageResult('Switched to environment `${environment.name}`'); } } diff --git a/puro/lib/src/commands/eval.dart b/puro/lib/src/commands/eval.dart index c5b6f39..b68ff81 100644 --- a/puro/lib/src/commands/eval.dart +++ b/puro/lib/src/commands/eval.dart @@ -24,7 +24,8 @@ class EvalCommand extends PuroCommand { 'import', aliases: ['imports'], abbr: 'i', - help: 'A package to import, this option accepts a shortened package ' + help: + 'A package to import, this option accepts a shortened package ' 'URI followed by one or more optional modifiers\n\n' 'Shortened names expand as follows:\n' " foo => import 'package:foo/foo.dart'\n" @@ -44,7 +45,8 @@ class EvalCommand extends PuroCommand { 'package', aliases: ['packages'], abbr: 'p', - help: 'A package to depend on, this option accepts the package name ' + help: + 'A package to depend on, this option accepts the package name ' 'optionally followed by a version constraint:\n' ' name[`=`][constraint]\n' 'The package is removed from the pubspec if constraint is "none"', @@ -82,8 +84,9 @@ class EvalCommand extends PuroCommand { final noCoreImports = argResults!['no-core'] as bool; final reset = argResults!['reset'] as bool; - final imports = - (argResults!['import'] as List).map(EvalImport.parse).toList(); + final imports = (argResults!['import'] as List) + .map(EvalImport.parse) + .toList(); final packages = argResults!['package'] as List; final extra = argResults!['extra'] as List; var code = argResults!.rest.join(' '); diff --git a/puro/lib/src/commands/flutter.dart b/puro/lib/src/commands/flutter.dart index 81601a7..f7e4834 100644 --- a/puro/lib/src/commands/flutter.dart +++ b/puro/lib/src/commands/flutter.dart @@ -28,8 +28,9 @@ class FlutterCommand extends PuroCommand { final log = PuroLogger.of(scope); final environment = await getProjectEnvOrDefault(scope: scope); log.v('Flutter SDK: ${environment.flutter.sdkDir.path}'); - final nonOptionArgs = - argResults!.arguments.where((e) => !e.startsWith('-')).toList(); + final nonOptionArgs = argResults!.arguments + .where((e) => !e.startsWith('-')) + .toList(); if (nonOptionArgs.isNotEmpty) { if (nonOptionArgs.first == 'upgrade') { runner.addMessage( diff --git a/puro/lib/src/commands/internal_generate_ast_parser.dart b/puro/lib/src/commands/internal_generate_ast_parser.dart index 6722b72..56b0573 100644 --- a/puro/lib/src/commands/internal_generate_ast_parser.dart +++ b/puro/lib/src/commands/internal_generate_ast_parser.dart @@ -33,23 +33,25 @@ class GenerateASTParserCommand extends PuroCommand { remoteUrl: config.dartSdkGitUrl, ); } - final binaryMdResult = await git.raw( - [ - 'log', - '--format=%H', - '8dbe716085d4942ce87bd34e933cfccf2d0f70ae..main', - '--', - 'pkg/kernel/binary.md' - ], - directory: sharedRepository, - ); - final commits = - (binaryMdResult.stdout as String).trim().split('\n').reversed.toList(); + final binaryMdResult = await git.raw([ + 'log', + '--format=%H', + '8dbe716085d4942ce87bd34e933cfccf2d0f70ae..main', + '--', + 'pkg/kernel/binary.md', + ], directory: sharedRepository); + final commits = (binaryMdResult.stdout as String) + .trim() + .split('\n') + .reversed + .toList(); - commits.add(await git.getCurrentCommitHash( - repository: sharedRepository, - branch: 'main', - )); + commits.add( + await git.getCurrentCommitHash( + repository: sharedRepository, + branch: 'main', + ), + ); final binaryMdDir = workingDir.childDirectory('binary-md'); if (!binaryMdDir.existsSync()) { @@ -60,8 +62,9 @@ class GenerateASTParserCommand extends PuroCommand { path: 'tools/VERSION', ref: line, ); - final major = RegExp(r'MAJOR (\d+)') - .firstMatch(utf8.decode(versionContents))![1]!; + final major = RegExp( + r'MAJOR (\d+)', + ).firstMatch(utf8.decode(versionContents))![1]!; if (major == '1') continue; final contents = await git.cat( repository: sharedRepository, @@ -85,8 +88,10 @@ class GenerateASTParserCommand extends PuroCommand { for (final childFile in binaryMdDir.listSync()) { if (childFile is! File || !childFile.basename.endsWith('.md')) continue; final contents = childFile.readAsStringSync(); - final commit = - childFile.basename.substring(0, childFile.basename.length - 3); + final commit = childFile.basename.substring( + 0, + childFile.basename.length - 3, + ); binaryMdCommits[commit] = contents; } @@ -125,16 +130,19 @@ class GenerateASTParserCommand extends PuroCommand { final componentFile = (result.value as List).singleWhere((e) { return e['type'] != null && e['type'][1] == 'ComponentFile'; }); - final version = int.parse((componentFile['type'][3] as List).singleWhere( - (e) => e['field'] != null && e['field'][1] == 'formatVersion')[ - 'field'][2] as String); + final version = int.parse( + (componentFile['type'][3] as List).singleWhere( + (e) => e['field'] != null && e['field'][1] == 'formatVersion', + )['field'][2] + as String, + ); verCommit[version] = commit; verSchema[version] = result.value; - astsJsonDir.childFile('v$version.json').writeAsStringSync( - prettyJsonEncoder.convert(result.value), - ); + astsJsonDir + .childFile('v$version.json') + .writeAsStringSync(prettyJsonEncoder.convert(result.value)); } commits.removeWhere((e) => !verCommit.values.contains(e)); @@ -147,10 +155,9 @@ class GenerateASTParserCommand extends PuroCommand { if (astsDir.existsSync()) astsDir.deleteSync(recursive: true); astsDir.createSync(recursive: true); for (final entry in verSchema.entries) { - final ast = generateAstForSchemas( - {entry.key: entry.value}, - comment: 'For schema ${verCommit[entry.key]}', - ); + final ast = generateAstForSchemas({ + entry.key: entry.value, + }, comment: 'For schema ${verCommit[entry.key]}'); astsDir.childFile('v${entry.key}.dart').writeAsStringSync(ast); } @@ -159,22 +166,17 @@ class GenerateASTParserCommand extends PuroCommand { if (diffsDir.existsSync()) diffsDir.deleteSync(recursive: true); diffsDir.createSync(recursive: true); for (final entry in verSchema.entries.skip(1)) { - final diff = await runProcess( - scope, - 'diff', - [ - '--context', - '-F', - '^class', - '--label', - 'v${entry.key}', - '--label', - 'v${entry.key - 1}', - astsDir.childFile('v${entry.key - 1}.dart').path, - astsDir.childFile('v${entry.key}.dart').path, - ], - debugLogging: false, - ); + final diff = await runProcess(scope, 'diff', [ + '--context', + '-F', + '^class', + '--label', + 'v${entry.key}', + '--label', + 'v${entry.key - 1}', + astsDir.childFile('v${entry.key - 1}.dart').path, + astsDir.childFile('v${entry.key}.dart').path, + ], debugLogging: false); if (diff.exitCode > 1) { return BasicMessageResult( 'Failed to generate diff:\n${diff.stderr}', @@ -190,19 +192,19 @@ class GenerateASTParserCommand extends PuroCommand { final releases = await getDartReleases(scope: scope); final allReleases = releases.releases.entries - .expand((r) => r.value.map((v) => DartRelease( - DartOS.current, - DartArch.current, - r.key, - v, - ))) + .expand( + (r) => r.value.map( + (v) => DartRelease(DartOS.current, DartArch.current, r.key, v), + ), + ) .toList(); // allReleases.removeWhere((e) => e.version.major < 2); // This release has no artifacts for some reason allReleases.removeWhere( - (e) => '${e.version}' == '1.24.0' && e.channel == DartChannel.dev); + (e) => '${e.version}' == '1.24.0' && e.channel == DartChannel.dev, + ); // print('releases: ${allReleases.length}'); // print('releases: ${allReleases.map((e) => e.name).join(',')}'); @@ -311,7 +313,8 @@ class GenerateASTParserCommand extends PuroCommand { String generateAstForSchemas(Map schemas, {String? comment}) { String fixName(String name, {bool lower = false}) { - var out = const { + var out = + const { 'VariableDeclarationPlain': 'VariableDeclaration', 'class': 'clazz', '8bitAlignment': 'byteAlignment', @@ -373,22 +376,17 @@ String generateAstForSchemas(Map schemas, {String? comment}) { ('Expression', 'IntegerLiteral'): 'IntegerLiteral', ( 'PositiveIntLiteral | NegativeIntLiteral | SpecializedIntLiteral | BigIntLiteral', - 'IntegerLiteral' + 'IntegerLiteral', ): 'IntegerLiteral', }[(from.name, to.name)]; if (result != null) { return getType(result); } else { - throw AssertionError( - 'Mismatch ${from.name} != ${to.name}', - ); + throw AssertionError('Mismatch ${from.name} != ${to.name}'); } } - const skipTypes = { - 'StringTable', - 'FileOffset', - }; + const skipTypes = {'StringTable', 'FileOffset'}; for (final entry in schemas.entries) { // print('Processing ${entry.key} (${verCommit[entry.key]})'); @@ -468,10 +466,8 @@ String generateAstForSchemas(Map schemas, {String? comment}) { final parentName = parentType.name; if (existingParentName != null && existingParentName != parentName) { - existing.parent = resolveMerge( - existing.parent!, - parentType, - ) as ClassType; + existing.parent = + resolveMerge(existing.parent!, parentType) as ClassType; } else { existing.parent = parentType; } @@ -626,13 +622,13 @@ class UnionType extends DartType { final firstName = first is UnionType ? first.name : (first is ClassType - ? (first as ClassType).parent!.name - : throw AssertionError()); + ? (first as ClassType).parent!.name + : throw AssertionError()); final secondName = second is UnionType ? second.name : (second is ClassType - ? (second as ClassType).parent!.name - : throw AssertionError()); + ? (second as ClassType).parent!.name + : throw AssertionError()); assert(firstName == secondName); return firstName; } diff --git a/puro/lib/src/commands/internal_generate_docs.dart b/puro/lib/src/commands/internal_generate_docs.dart index 904bd66..b00d2e3 100644 --- a/puro/lib/src/commands/internal_generate_docs.dart +++ b/puro/lib/src/commands/internal_generate_docs.dart @@ -42,9 +42,9 @@ class GenerateDocsCommand extends PuroCommand { .childFile('commands.md') .writeAsString(generateCommands()); - await puroDir.childFile('CHANGELOG.md').copy( - referenceDir.childFile('changelog.md').path, - ); + await puroDir + .childFile('CHANGELOG.md') + .copy(referenceDir.childFile('changelog.md').path); if (argResults!['deploy'] as bool) { // Replace master in the installation instructions with the latest version @@ -99,10 +99,12 @@ class GenerateDocsCommand extends PuroCommand { final buffer = StringBuffer(); for (final option in options) { buffer.write('#### '); - buffer.writeln([ - if (option.abbr != null) '`-${option.abbr}`', - '`${optionString(option)}`', - ].join(', ')); + buffer.writeln( + [ + if (option.abbr != null) '`-${option.abbr}`', + '`${optionString(option)}`', + ].join(', '), + ); buffer.writeln(); if (option.help != null) { buffer.writeln(option.help); @@ -143,8 +145,9 @@ class GenerateDocsCommand extends PuroCommand { } else if (option.isMultiple) { final defaultsTo = option.defaultsTo as List?; if (defaultsTo != null && defaultsTo.isNotEmpty) { - final defaults = - defaultsTo.map((dynamic value) => '`"$value"`').join(', '); + final defaults = defaultsTo + .map((dynamic value) => '`"$value"`') + .join(', '); buffer.writeln('(defaults to $defaults)'); buffer.writeln(); } @@ -162,8 +165,10 @@ class GenerateDocsCommand extends PuroCommand { buffer.writeln(); for (final command in runner.commands.values.toSet()) { if (command.hidden) continue; - buffer.writeln('## ${command.name.substring(0, 1).toUpperCase()}' - '${command.name.substring(1)}'); + buffer.writeln( + '## ${command.name.substring(0, 1).toUpperCase()}' + '${command.name.substring(1)}', + ); buffer.writeln(); buffer.writeln('```sh'); buffer.writeln('${command.invocation}'); @@ -171,8 +176,9 @@ class GenerateDocsCommand extends PuroCommand { buffer.writeln(); buffer.writeln(command.description); buffer.writeln(); - final options = command.argParser.options.values - .where((e) => e.name != 'help' && !e.hide); + final options = command.argParser.options.values.where( + (e) => e.name != 'help' && !e.hide, + ); if (options.isNotEmpty) { buffer.writeln('#### Options'); buffer.writeln(); diff --git a/puro/lib/src/commands/ls_versions.dart b/puro/lib/src/commands/ls_versions.dart index 32c8e42..27d64ae 100644 --- a/puro/lib/src/commands/ls_versions.dart +++ b/puro/lib/src/commands/ls_versions.dart @@ -44,8 +44,10 @@ class LsVersionsCommand extends PuroCommand { FlutterReleasesModel? flutterVersions; if (!force) { - flutterVersions = - await getCachedFlutterReleases(scope: scope, unlessStale: true); + flutterVersions = await getCachedFlutterReleases( + scope: scope, + unlessStale: true, + ); } flutterVersions ??= await fetchFlutterReleases(scope: scope); @@ -54,8 +56,12 @@ class LsVersionsCommand extends PuroCommand { ..sort((a, b) { return parsedVersions .putIfAbsent(b.version, () => tryParseVersion(b.version)!) - .compareTo(parsedVersions.putIfAbsent( - a.version, () => tryParseVersion(a.version)!)); + .compareTo( + parsedVersions.putIfAbsent( + a.version, + () => tryParseVersion(a.version)!, + ), + ); }); final now = clock.now(); @@ -69,7 +75,8 @@ class LsVersionsCommand extends PuroCommand { // Can contain duplicates for each architecture if (version == lastVersion) continue; - final isPreviousPatch = lastVersion != null && + final isPreviousPatch = + lastVersion != null && version.major == lastVersion.major && version.minor == lastVersion.minor; lastVersion = version; @@ -87,53 +94,52 @@ class LsVersionsCommand extends PuroCommand { 'beta': latestReleasesFor('beta'), }; - return BasicMessageResult.format( - (format) { - List> formatReleases(List releases) { - return [ - for (final release in releases) - [ - 'Flutter ${release.version}', - format.color(' | ', foregroundColor: Ansi8BitColor.grey), - DateTime.parse(release.releaseDate).difference(now).pretty( - before: '', - abbr: true, - ), - format.color(' | ', foregroundColor: Ansi8BitColor.grey), - release.hash.substring(0, 10), - format.color(' | ', foregroundColor: Ansi8BitColor.grey), - 'Dart ${release.dartSdkVersion.split(' ').first}', - ], - ]; - } - - final formattedReleases = >>{ - for (final entry in channelReleases.entries) - entry.key: formatReleases(entry.value), - }; - - final colWidths = List.generate( - formattedReleases.values.first.first.length, - (index) { - return formattedReleases.values.fold(0, (n, rows) { - return max( - n, - rows.fold(0, - (n, row) => max(n, stripAnsiEscapes(row[index]).length))); - }); - }, - ); - + return BasicMessageResult.format((format) { + List> formatReleases(List releases) { return [ - for (final entry in formattedReleases.entries) ...[ - 'Latest ${entry.key} releases:', - for (final row in entry.value) - '${row.mapWithIndex((s, i) => padRightColored(s, colWidths[i])).join()}', - '', - ], - ].join('\n').trim(); - }, - type: CompletionType.info, - ); + for (final release in releases) + [ + 'Flutter ${release.version}', + format.color(' | ', foregroundColor: Ansi8BitColor.grey), + DateTime.parse( + release.releaseDate, + ).difference(now).pretty(before: '', abbr: true), + format.color(' | ', foregroundColor: Ansi8BitColor.grey), + release.hash.substring(0, 10), + format.color(' | ', foregroundColor: Ansi8BitColor.grey), + 'Dart ${release.dartSdkVersion.split(' ').first}', + ], + ]; + } + + final formattedReleases = >>{ + for (final entry in channelReleases.entries) + entry.key: formatReleases(entry.value), + }; + + final colWidths = List.generate( + formattedReleases.values.first.first.length, + (index) { + return formattedReleases.values.fold(0, (n, rows) { + return max( + n, + rows.fold( + 0, + (n, row) => max(n, stripAnsiEscapes(row[index]).length), + ), + ); + }); + }, + ); + + return [ + for (final entry in formattedReleases.entries) ...[ + 'Latest ${entry.key} releases:', + for (final row in entry.value) + '${row.mapWithIndex((s, i) => padRightColored(s, colWidths[i])).join()}', + '', + ], + ].join('\n').trim(); + }, type: CompletionType.info); } } diff --git a/puro/lib/src/commands/prepare.dart b/puro/lib/src/commands/prepare.dart index 29d98f4..5e76bc0 100644 --- a/puro/lib/src/commands/prepare.dart +++ b/puro/lib/src/commands/prepare.dart @@ -19,7 +19,8 @@ class PrepareCommand extends PuroCommand { ) ..addMultiOption( 'platform', - help: 'Precache artifacts for the provided platforms (android, ios, etc).', + help: + 'Precache artifacts for the provided platforms (android, ios, etc).', valueHelp: 'name', allowed: preparePlatformOptions.toList()..sort(), ); @@ -46,13 +47,15 @@ class PrepareCommand extends PuroCommand { final force = argResults!['force'] as bool; final allPlatforms = argResults!['all-platforms'] as bool; - final requestedPlatforms = - (argResults!['platform'] as List).map((e) => e.toLowerCase()); + final requestedPlatforms = (argResults!['platform'] as List).map( + (e) => e.toLowerCase(), + ); final sortedRequested = sortPreparePlatforms(requestedPlatforms); + final defaultPlatforms = defaultPreparePlatforms(); final platforms = sortedRequested.isNotEmpty ? sortedRequested - : (allPlatforms ? [] : defaultPreparePlatforms()); + : (allPlatforms ? [] : defaultPlatforms); log.d( 'Preparing environment `${environment.name}` for platforms: ' @@ -71,8 +74,8 @@ class PrepareCommand extends PuroCommand { final platformSummary = allPlatforms ? 'all platforms' : (platforms.isEmpty - ? 'default platforms (${defaultPreparePlatforms().join(', ')})' - : platforms.join(', ')); + ? 'default platforms (${defaultPlatforms.join(', ')})' + : platforms.join(', ')); return BasicMessageResult( 'Prepared environment `${environment.name}` (${platformSummary}${force ? ', forced' : ''})', diff --git a/puro/lib/src/commands/puro_install.dart b/puro/lib/src/commands/puro_install.dart index 4020b14..26c97c9 100644 --- a/puro/lib/src/commands/puro_install.dart +++ b/puro/lib/src/commands/puro_install.dart @@ -56,14 +56,11 @@ class PuroInstallCommand extends PuroCommand { final force = argResults!['force'] as bool; final promote = argResults!['promote'] as bool; final profileOverride = argResults!['profile'] as String?; - final updatePath = - argResults!.wasParsed('path') ? argResults!['path'] as bool : null; + final updatePath = argResults!.wasParsed('path') + ? argResults!['path'] as bool + : null; - await ensurePuroInstalled( - scope: scope, - force: force, - promote: promote, - ); + await ensurePuroInstalled(scope: scope, force: force, promote: promote); final PuroGlobalPrefsModel prefs = await updateGlobalPrefs( scope: scope, @@ -110,17 +107,16 @@ class PuroInstallCommand extends PuroCommand { if (Platform.isLinux || Platform.isMacOS) { final profile = await installProfileEnv( scope: scope, - profileOverride: - prefs.hasProfileOverride() ? prefs.profileOverride : null, + profileOverride: prefs.hasProfileOverride() + ? prefs.profileOverride + : null, ); profilePath = profile?.path; if (profilePath != null && profilePath.startsWith(homeDir)) { profilePath = '~' + profilePath.substring(homeDir.length); } } else if (Platform.isWindows) { - updatedWindowsRegistry = await tryUpdateWindowsPath( - scope: scope, - ); + updatedWindowsRegistry = await tryUpdateWindowsPath(scope: scope); } } @@ -130,17 +126,14 @@ class PuroInstallCommand extends PuroCommand { if (envDir.basename == 'default') continue; final environment = config.getEnv(envDir.basename); if (!environment.flutterDir.childDirectory('.git').existsSync()) continue; - await runOptional( - scope, - '`${environment.name}` post-upgrade', - () async { - await installEnvShims(scope: scope, environment: environment); - }, - ); + await runOptional(scope, '`${environment.name}` post-upgrade', () async { + await installEnvShims(scope: scope, environment: environment); + }); } - final externalMessage = - await detectExternalFlutterInstallations(scope: scope); + final externalMessage = await detectExternalFlutterInstallations( + scope: scope, + ); final updateMessage = await checkIfUpdateAvailable( scope: scope, @@ -153,7 +146,8 @@ class PuroInstallCommand extends PuroCommand { if (updateMessage != null) updateMessage, if (profilePath != null) CommandMessage( - 'Updated PATH in $profilePath, reopen your terminal or `source $profilePath` for it to take effect'), + 'Updated PATH in $profilePath, reopen your terminal or `source $profilePath` for it to take effect', + ), if (updatedWindowsRegistry) CommandMessage( 'Updated PATH in the Windows registry, reopen your terminal for it to take effect', diff --git a/puro/lib/src/commands/puro_uninstall.dart b/puro/lib/src/commands/puro_uninstall.dart index d70e55f..75b73de 100644 --- a/puro/lib/src/commands/puro_uninstall.dart +++ b/puro/lib/src/commands/puro_uninstall.dart @@ -52,14 +52,13 @@ class PuroUninstallCommand extends PuroCommand { if (Platform.isLinux || Platform.isMacOS) { final profile = await uninstallProfileEnv( scope: scope, - profileOverride: - prefs.hasProfileOverride() ? prefs.profileOverride : null, + profileOverride: prefs.hasProfileOverride() + ? prefs.profileOverride + : null, ); profilePath = profile?.path.replaceAll(homeDir, '~'); } else if (Platform.isWindows) { - updatedWindowsRegistry = await tryCleanWindowsPath( - scope: scope, - ); + updatedWindowsRegistry = await tryCleanWindowsPath(scope: scope); } if (profilePath == null && !updatedWindowsRegistry) { @@ -71,7 +70,8 @@ class PuroUninstallCommand extends PuroCommand { return BasicMessageResult.list([ if (profilePath != null) CommandMessage( - 'Removed Puro from PATH in $profilePath, reopen your terminal for it to take effect'), + 'Removed Puro from PATH in $profilePath, reopen your terminal for it to take effect', + ), if (updatedWindowsRegistry) CommandMessage( 'Removed Puro from PATH in the Windows registry, reopen your terminal for it to take effect', diff --git a/puro/lib/src/commands/puro_upgrade.dart b/puro/lib/src/commands/puro_upgrade.dart index c7a5a59..2b936bf 100644 --- a/puro/lib/src/commands/puro_upgrade.dart +++ b/puro/lib/src/commands/puro_upgrade.dart @@ -51,21 +51,18 @@ class PuroUpgradeCommand extends PuroCommand { var targetVersionString = unwrapSingleOptionalArgument(); if (puroVersion.type == PuroInstallationType.pub) { - final result = await runProcess( - scope, - Platform.resolvedExecutable, - ['pub', 'global', 'activate', 'puro'], - ); + final result = await runProcess(scope, Platform.resolvedExecutable, [ + 'pub', + 'global', + 'activate', + 'puro', + ]); if (result.exitCode == 0) { final stdout = result.stdout as String; if (stdout.contains('already activated at newest available version')) { - return BasicMessageResult( - 'Puro is up to date with $currentVersion', - ); + return BasicMessageResult('Puro is up to date with $currentVersion'); } else { - return BasicMessageResult( - 'Upgraded puro to latest pub version', - ); + return BasicMessageResult('Upgraded puro to latest pub version'); } } else { return BasicMessageResult( @@ -86,16 +83,18 @@ class PuroUpgradeCommand extends PuroCommand { final exitCode = await upgradePuro( scope: scope, targetVersion: 'master', - path: - argResults!.wasParsed('path') ? argResults!['path'] as bool : null, + path: argResults!.wasParsed('path') + ? argResults!['path'] as bool + : null, ); await runner.exitPuro(exitCode); } final Version targetVersion; if (targetVersionString == null) { - final latestVersionResponse = - await http.get(config.puroBuildsUrl.append(path: 'latest')); + final latestVersionResponse = await http.get( + config.puroBuildsUrl.append(path: 'latest'), + ); HttpException.ensureSuccess(latestVersionResponse); targetVersionString = latestVersionResponse.body.trim(); targetVersion = Version.parse(targetVersionString); @@ -110,9 +109,7 @@ class PuroUpgradeCommand extends PuroCommand { } else { targetVersion = Version.parse(targetVersionString); if (currentVersion == targetVersion && !force) { - return BasicMessageResult( - 'Puro is the desired version $targetVersion', - ); + return BasicMessageResult('Puro is the desired version $targetVersion'); } } diff --git a/puro/lib/src/commands/repl.dart b/puro/lib/src/commands/repl.dart index 458cacc..bc6dcb5 100644 --- a/puro/lib/src/commands/repl.dart +++ b/puro/lib/src/commands/repl.dart @@ -62,20 +62,20 @@ class ReplCommand extends PuroCommand { extra: extra, ); - unawaited(worker.onExit.then((exitCode) async { - if (exitCode != 0) { - CommandMessage( - 'Subprocess exited with code $exitCode', - type: CompletionType.alert, - ).queue(scope); - } - await runner.exitPuro(exitCode); - })); + unawaited( + worker.onExit.then((exitCode) async { + if (exitCode != 0) { + CommandMessage( + 'Subprocess exited with code $exitCode', + type: CompletionType.alert, + ).queue(scope); + } + await runner.exitPuro(exitCode); + }), + ); final terminal = Terminal.of(scope); - final console = Console.scrolling( - recordBlanks: false, - ); + final console = Console.scrolling(recordBlanks: false); var didBreak = false; while (true) { @@ -102,10 +102,12 @@ class ReplCommand extends PuroCommand { stdout.writeln(result); } } catch (e, bt) { - stdout.writeln(terminal.format.complete( - '$e${e is EvalError ? '' : '\n$bt'}', - type: CompletionType.failure, - )); + stdout.writeln( + terminal.format.complete( + '$e${e is EvalError ? '' : '\n$bt'}', + type: CompletionType.failure, + ), + ); } } diff --git a/puro/lib/src/commands/version.dart b/puro/lib/src/commands/version.dart index 55908a5..691aecb 100644 --- a/puro/lib/src/commands/version.dart +++ b/puro/lib/src/commands/version.dart @@ -13,11 +13,7 @@ class VersionCommand extends PuroCommand { negatable: false, help: 'Print just the version to stdout and exit', ); - argParser.addFlag( - 'release', - negatable: false, - hide: true, - ); + argParser.addFlag('release', negatable: false, hide: true); } @override @@ -39,8 +35,9 @@ class VersionCommand extends PuroCommand { stdout.write('${puroVersion.semver}'); await runner.exitPuro(0); } - final externalMessage = - await detectExternalFlutterInstallations(scope: scope); + final externalMessage = await detectExternalFlutterInstallations( + scope: scope, + ); final updateMessage = await checkIfUpdateAvailable( scope: scope, runner: runner, diff --git a/puro/lib/src/config.dart b/puro/lib/src/config.dart index 2937d19..231dbca 100644 --- a/puro/lib/src/config.dart +++ b/puro/lib/src/config.dart @@ -105,14 +105,12 @@ class PuroConfig { ); } - final absoluteProjectDir = - projectDir == null ? null : fileSystem.directory(projectDir).absolute; + final absoluteProjectDir = projectDir == null + ? null + : fileSystem.directory(projectDir).absolute; - var resultProjectDir = absoluteProjectDir ?? - findProjectDir( - currentDir, - 'pubspec.yaml', - ); + var resultProjectDir = + absoluteProjectDir ?? findProjectDir(currentDir, 'pubspec.yaml'); // Puro looks for a suitable project root in the following order: // 1. Directory specified in `--project` @@ -122,16 +120,11 @@ class PuroConfig { // If Puro finds a grandparent and tries to access the parentProjectDir with // dotfileForWriting, it throws an error indicating the selection is // ambiguous. - final Directory? parentProjectDir = absoluteProjectDir ?? - findProjectDir( - currentDir, - ProjectConfig.dotfileName, - ) ?? + final Directory? parentProjectDir = + absoluteProjectDir ?? + findProjectDir(currentDir, ProjectConfig.dotfileName) ?? (resultProjectDir != null - ? findProjectDir( - resultProjectDir.parent, - 'pubspec.yaml', - ) + ? findProjectDir(resultProjectDir.parent, 'pubspec.yaml') : null) ?? resultProjectDir; @@ -139,8 +132,9 @@ class PuroConfig { log.d('puroRootDir: $puroRoot'); puroRoot.createSync(recursive: true); - puroRoot = - fileSystem.directory(puroRoot.resolveSymbolicLinksSync()).absolute; + puroRoot = fileSystem + .directory(puroRoot.resolveSymbolicLinksSync()) + .absolute; log.d('puroRoot (resolved): $puroRoot'); if (environmentOverride == null) { @@ -168,7 +162,8 @@ class PuroConfig { } } - flutterStorageBaseUrl ??= (globalPrefs.hasFlutterStorageBaseUrl() + flutterStorageBaseUrl ??= + (globalPrefs.hasFlutterStorageBaseUrl() ? globalPrefs.flutterStorageBaseUrl : null) ?? 'https://storage.googleapis.com'; @@ -178,8 +173,10 @@ class PuroConfig { pubCache ??= pubCacheOverride; } pubCache ??= globalPrefs.hasPubCacheDir() ? globalPrefs.pubCacheDir : null; - pubCache ??= - puroRoot.childDirectory('shared').childDirectory('pub_cache').path; + pubCache ??= puroRoot + .childDirectory('shared') + .childDirectory('pub_cache') + .path; shouldSkipCacheSync ??= Platform.environment['PURO_SKIP_CACHE_SYNC']?.isNotEmpty ?? false; @@ -194,13 +191,16 @@ class PuroConfig { homeDir: fileSystem.directory(homeDir), projectDir: resultProjectDir, parentProjectDir: parentProjectDir, - flutterGitUrl: flutterGitUrl ?? + flutterGitUrl: + flutterGitUrl ?? (globalPrefs.hasFlutterGitUrl() ? globalPrefs.flutterGitUrl : null) ?? 'https://github.com/flutter/flutter.git', - engineGitUrl: engineGitUrl ?? + engineGitUrl: + engineGitUrl ?? (globalPrefs.hasEngineGitUrl() ? globalPrefs.engineGitUrl : null) ?? 'https://github.com/flutter/engine.git', - dartSdkGitUrl: dartSdkGitUrl ?? + dartSdkGitUrl: + dartSdkGitUrl ?? (globalPrefs.hasDartSdkGitUrl() ? globalPrefs.dartSdkGitUrl : null) ?? 'https://github.com/dart-lang/sdk.git', releasesJsonUrl: Uri.parse( @@ -213,13 +213,15 @@ class PuroConfig { flutterStorageBaseUrl: Uri.parse(flutterStorageBaseUrl), environmentOverride: environmentOverride, puroBuildsUrl: Uri.parse( - (globalPrefs.hasPuroBuildsUrl() ? globalPrefs.puroBuildsUrl : null) ?? - 'https://puro.dev/builds'), + (globalPrefs.hasPuroBuildsUrl() ? globalPrefs.puroBuildsUrl : null) ?? + 'https://puro.dev/builds', + ), buildTarget: globalPrefs.hasPuroBuildTarget() ? PuroBuildTarget.fromString(globalPrefs.puroBuildTarget) : PuroBuildTarget.query(), enableShims: enableShims, - shouldInstall: shouldInstall ?? + shouldInstall: + shouldInstall ?? (!globalPrefs.hasShouldInstall() || globalPrefs.shouldInstall), shouldSkipCacheSync: shouldSkipCacheSync, ); @@ -296,26 +298,34 @@ class PuroConfig { late final Directory sharedFlutterDir = sharedDir.childDirectory('flutter'); late final Directory sharedEngineDir = sharedDir.childDirectory('engine'); late final Directory sharedDartSdkDir = sharedDir.childDirectory('dart-sdk'); - late final Directory sharedDartReleaseDir = - sharedDir.childDirectory('dart-release'); + late final Directory sharedDartReleaseDir = sharedDir.childDirectory( + 'dart-release', + ); late final Directory sharedCachesDir = sharedDir.childDirectory('caches'); late final Directory sharedGClientDir = sharedDir.childDirectory('gclient'); late final Directory pubCacheBinDir = legacyPubCacheDir.childDirectory('bin'); - late final Directory sharedFlutterToolsDir = - sharedDir.childDirectory('flutter_tools'); - late final File puroExecutableFile = - binDir.childFile(buildTarget.executableName); - late final File puroTrampolineFile = - binDir.childFile(buildTarget.trampolineName); + late final Directory sharedFlutterToolsDir = sharedDir.childDirectory( + 'flutter_tools', + ); + late final File puroExecutableFile = binDir.childFile( + buildTarget.executableName, + ); + late final File puroTrampolineFile = binDir.childFile( + buildTarget.trampolineName, + ); late final File puroDartShimFile = binDir.childFile(buildTarget.dartName); - late final File puroFlutterShimFile = - binDir.childFile(buildTarget.flutterName); - late final File puroExecutableTempFile = - binDir.childFile('${buildTarget.executableName}.tmp'); - late final File cachedReleasesJsonFile = - puroRoot.childFile(releasesJsonUrl.pathSegments.last); - late final File cachedDartReleasesJsonFile = - puroRoot.childFile('dart_releases.json'); + late final File puroFlutterShimFile = binDir.childFile( + buildTarget.flutterName, + ); + late final File puroExecutableTempFile = binDir.childFile( + '${buildTarget.executableName}.tmp', + ); + late final File cachedReleasesJsonFile = puroRoot.childFile( + releasesJsonUrl.pathSegments.last, + ); + late final File cachedDartReleasesJsonFile = puroRoot.childFile( + 'dart_releases.json', + ); late final File defaultEnvNameFile = puroRoot.childFile('default_env'); late final Link defaultEnvLink = envsDir.childLink('default'); late final Uri puroLatestVersionUrl = puroBuildsUrl.append(path: 'latest'); @@ -376,18 +386,20 @@ class PuroConfig { ); } if (patched) { - return FlutterCacheConfig(sharedCachesDir.childDirectory( - '${engineCommit}_patched', - )); + return FlutterCacheConfig( + sharedCachesDir.childDirectory('${engineCommit}_patched'), + ); } else { return FlutterCacheConfig(sharedCachesDir.childDirectory(engineCommit)); } } DartSdkConfig getDartRelease(DartRelease release) { - return DartSdkConfig(sharedDartReleaseDir - .childDirectory(release.name) - .childDirectory('dart-sdk')); + return DartSdkConfig( + sharedDartReleaseDir + .childDirectory(release.name) + .childDirectory('dart-sdk'), + ); } Uri? tryGetFlutterGitDownloadUrl({ @@ -401,10 +413,7 @@ class PuroConfig { flutterGitUrl.endsWith('.git')) { return Uri.https( 'raw.githubusercontent.com', - '${flutterGitUrl.substring( - isHttp ? httpPrefix.length : sshPrefix.length, - flutterGitUrl.length - 4, - )}/$commit/$path', + '${flutterGitUrl.substring(isHttp ? httpPrefix.length : sshPrefix.length, flutterGitUrl.length - 4)}/$commit/$path', ); } return null; @@ -421,10 +430,7 @@ class PuroConfig { engineGitUrl.endsWith('.git')) { return Uri.https( 'raw.githubusercontent.com', - '${engineGitUrl.substring( - isHttp ? httpPrefix.length : sshPrefix.length, - engineGitUrl.length - 4, - )}/$commit/$path', + '${engineGitUrl.substring(isHttp ? httpPrefix.length : sshPrefix.length, engineGitUrl.length - 4)}/$commit/$path', ); } return null; @@ -546,10 +552,7 @@ class ProjectConfig { } class EnvConfig { - EnvConfig({ - required this.parentConfig, - required this.envDir, - }); + EnvConfig({required this.parentConfig, required this.envDir}); final PuroConfig parentConfig; final Directory envDir; @@ -580,9 +583,7 @@ class EnvConfig { // lets you change it with an environment variable String get flutterToolArgs => ''; - Future readPrefs({ - required Scope scope, - }) async { + Future readPrefs({required Scope scope}) async { final model = PuroEnvPrefsModel(); if (prefsJsonFile.existsSync()) { final contents = await readAtomic(scope: scope, file: prefsJsonFile); @@ -596,25 +597,20 @@ class EnvConfig { required FutureOr Function(PuroEnvPrefsModel prefs) fn, bool background = false, }) { - return lockFile( - scope, - prefsJsonFile, - (handle) async { - final model = PuroEnvPrefsModel(); - String? contents; - if (handle.lengthSync() > 0) { - contents = handle.readAllAsStringSync(); - model.mergeFromProto3Json(jsonDecode(contents)); - } - await fn(model); - final newContents = prettyJsonEncoder.convert(model.toProto3Json()); - if (contents != newContents) { - handle.writeAllStringSync(newContents); - } - return model; - }, - mode: FileMode.append, - ); + return lockFile(scope, prefsJsonFile, (handle) async { + final model = PuroEnvPrefsModel(); + String? contents; + if (handle.lengthSync() > 0) { + contents = handle.readAllAsStringSync(); + model.mergeFromProto3Json(jsonDecode(contents)); + } + await fn(model); + final newContents = prettyJsonEncoder.convert(model.toProto3Json()); + if (contents != newContents) { + handle.writeAllStringSync(newContents); + } + return model; + }, mode: FileMode.append); } } @@ -625,28 +621,36 @@ class FlutterConfig { late final Directory binDir = sdkDir.childDirectory('bin'); late final Directory packagesDir = sdkDir.childDirectory('packages'); - late final File flutterScript = - binDir.childFile(Platform.isWindows ? 'flutter.bat' : 'flutter'); - late final File dartScript = - binDir.childFile(Platform.isWindows ? 'dart.bat' : 'dart'); + late final File flutterScript = binDir.childFile( + Platform.isWindows ? 'flutter.bat' : 'flutter', + ); + late final File dartScript = binDir.childFile( + Platform.isWindows ? 'dart.bat' : 'dart', + ); late final Directory binInternalDir = binDir.childDirectory('internal'); late final Directory cacheDir = binDir.childDirectory('cache'); late final FlutterCacheConfig cache = FlutterCacheConfig(cacheDir); - late final File engineVersionFile = - binInternalDir.childFile('engine.version'); - late final Directory flutterToolsDir = - packagesDir.childDirectory('flutter_tools'); - late final File flutterToolsScriptFile = - flutterToolsDir.childDirectory('bin').childFile('flutter_tools.dart'); - late final File flutterToolsPubspecYamlFile = - flutterToolsDir.childFile('pubspec.yaml'); - late final File flutterToolsPubspecLockFile = - flutterToolsDir.childFile('pubspec.lock'); + late final File engineVersionFile = binInternalDir.childFile( + 'engine.version', + ); + late final Directory flutterToolsDir = packagesDir.childDirectory( + 'flutter_tools', + ); + late final File flutterToolsScriptFile = flutterToolsDir + .childDirectory('bin') + .childFile('flutter_tools.dart'); + late final File flutterToolsPubspecYamlFile = flutterToolsDir.childFile( + 'pubspec.yaml', + ); + late final File flutterToolsPubspecLockFile = flutterToolsDir.childFile( + 'pubspec.lock', + ); late final File flutterToolsPackageConfigJsonFile = flutterToolsDir .childDirectory('.dart_tool') .childFile('package_config.json'); - late final File flutterToolsLegacyPackagesFile = - flutterToolsDir.childFile('.packages'); + late final File flutterToolsLegacyPackagesFile = flutterToolsDir.childFile( + '.packages', + ); late final File legacyVersionFile = sdkDir.childFile('version'); String? get engineVersion => engineVersionFile.existsSync() @@ -668,12 +672,14 @@ class FlutterCacheConfig { late final Directory dartSdkDir = cacheDir.childDirectory('dart-sdk'); late final DartSdkConfig dartSdk = DartSdkConfig(dartSdkDir); - late final File flutterToolsStampFile = - cacheDir.childFile('flutter_tools.stamp'); + late final File flutterToolsStampFile = cacheDir.childFile( + 'flutter_tools.stamp', + ); late final File engineStampFile = cacheDir.childFile('engine.stamp'); late final File engineRealmFile = cacheDir.childFile('engine.realm'); - late final File engineVersionFile = - cacheDir.childFile('engine-dart-sdk.stamp'); + late final File engineVersionFile = cacheDir.childFile( + 'engine-dart-sdk.stamp', + ); String? get engineVersion => engineVersionFile.existsSync() ? engineVersionFile.readAsStringSync().trim() : null; @@ -692,13 +698,15 @@ class DartSdkConfig { late final Directory binDir = sdkDir.childDirectory('bin'); - late final File dartExecutable = - binDir.childFile(Platform.isWindows ? 'dart.exe' : 'dart'); + late final File dartExecutable = binDir.childFile( + Platform.isWindows ? 'dart.exe' : 'dart', + ); // This no longer exists on recent versions of Dart where we instead use // `dart pub`. - late final File oldPubExecutable = - binDir.childFile(Platform.isWindows ? 'pub.bat' : 'pub'); + late final File oldPubExecutable = binDir.childFile( + Platform.isWindows ? 'pub.bat' : 'pub', + ); late final Directory libDir = sdkDir.childDirectory('lib'); late final Directory internalLibDir = libDir.childDirectory('_internal'); @@ -786,9 +794,7 @@ void ensureValidEnvName(String name) { const prettyJsonEncoder = JsonEncoder.withIndent(' '); -Future _readGlobalPrefs({ - required Scope scope, -}) async { +Future _readGlobalPrefs({required Scope scope}) async { final model = PuroGlobalPrefsModel(); final file = scope.read(globalPrefsJsonFileProvider); if (file.existsSync()) { @@ -805,28 +811,23 @@ Future _updateGlobalPrefs({ }) { final file = scope.read(globalPrefsJsonFileProvider); file.parent.createSync(recursive: true); - return lockFile( - scope, - file, - (handle) async { - final model = PuroGlobalPrefsModel(); - String? contents; - if (handle.lengthSync() > 0) { - contents = handle.readAllAsStringSync(); - model.mergeFromProto3Json(jsonDecode(contents)); - } - await fn(model); - if (!model.hasLegacyPubCache()) { - model.legacyPubCache = !scope.read(isFirstRunProvider); - } - final newContents = prettyJsonEncoder.convert(model.toProto3Json()); - if (contents != newContents) { - handle.writeAllStringSync(newContents); - } - return model; - }, - mode: FileMode.append, - ); + return lockFile(scope, file, (handle) async { + final model = PuroGlobalPrefsModel(); + String? contents; + if (handle.lengthSync() > 0) { + contents = handle.readAllAsStringSync(); + model.mergeFromProto3Json(jsonDecode(contents)); + } + await fn(model); + if (!model.hasLegacyPubCache()) { + model.legacyPubCache = !scope.read(isFirstRunProvider); + } + final newContents = prettyJsonEncoder.convert(model.toProto3Json()); + if (contents != newContents) { + handle.writeAllStringSync(newContents); + } + return model; + }, mode: FileMode.append); } final globalPrefsJsonFileProvider = Provider.late(); @@ -835,9 +836,7 @@ final globalPrefsProvider = Provider>( (scope) => _readGlobalPrefs(scope: scope), ); -Future readGlobalPrefs({ - required Scope scope, -}) { +Future readGlobalPrefs({required Scope scope}) { return scope.read(globalPrefsProvider); } @@ -891,9 +890,7 @@ Future cleanDotfiles({required Scope scope}) { ); } -Future>> getAllDotfiles({ - required Scope scope, -}) async { +Future>> getAllDotfiles({required Scope scope}) async { final log = PuroLogger.of(scope); final config = PuroConfig.of(scope); final prefs = await readGlobalPrefs(scope: scope); @@ -910,9 +907,9 @@ Future>> getAllDotfiles({ final model = PuroDotfileModel.create(); model.mergeFromProto3Json(data); if (model.hasEnv()) { - result.putIfAbsent(model.env, () => {}).add( - dotfile.resolveSymbolicLinksSync(), - ); + result + .putIfAbsent(model.env, () => {}) + .add(dotfile.resolveSymbolicLinksSync()); } } catch (exception, stackTrace) { log.w('Error while reading $path'); @@ -924,10 +921,8 @@ Future>> getAllDotfiles({ await cleanDotfiles(scope: scope); } return result.map( - (key, value) => MapEntry( - key, - value.map((e) => config.fileSystem.file(e)).toList(), - ), + (key, value) => + MapEntry(key, value.map((e) => config.fileSystem.file(e)).toList()), ); } diff --git a/puro/lib/src/engine/build_env.dart b/puro/lib/src/engine/build_env.dart index 207b7b8..51ce1e3 100644 --- a/puro/lib/src/engine/build_env.dart +++ b/puro/lib/src/engine/build_env.dart @@ -47,12 +47,14 @@ Future runBuildEnvShell({ terminal ..flushStatus() - ..writeln(terminal.format.color( - '[ Running ${command![0]} with engine build environment,\n' - ' type `exit` to return to the normal shell ]\n', - bold: true, - foregroundColor: Ansi8BitColor.blue, - )); + ..writeln( + terminal.format.color( + '[ Running ${command![0]} with engine build environment,\n' + ' type `exit` to return to the normal shell ]\n', + bold: true, + foregroundColor: Ansi8BitColor.blue, + ), + ); } final process = await startProcess( @@ -70,11 +72,13 @@ Future runBuildEnvShell({ if (defaultShell) { terminal ..flushStatus() - ..writeln(terminal.format.color( - '\n[ Returning from engine build shell ]', - bold: true, - foregroundColor: Ansi8BitColor.blue, - )); + ..writeln( + terminal.format.color( + '\n[ Returning from engine build shell ]', + bold: true, + foregroundColor: Ansi8BitColor.blue, + ), + ); } return exitCode; diff --git a/puro/lib/src/engine/dart_version.dart b/puro/lib/src/engine/dart_version.dart index 76d562f..2c1a9d8 100644 --- a/puro/lib/src/engine/dart_version.dart +++ b/puro/lib/src/engine/dart_version.dart @@ -56,8 +56,5 @@ Future getEngineDartCommit({ ); // Try again after cloning the repository - return getEngineDartCommit( - scope: scope, - engineCommit: engineCommit, - ); + return getEngineDartCommit(scope: scope, engineCommit: engineCommit); } diff --git a/puro/lib/src/engine/depot_tools.dart b/puro/lib/src/engine/depot_tools.dart index 53e8baa..e49d402 100644 --- a/puro/lib/src/engine/depot_tools.dart +++ b/puro/lib/src/engine/depot_tools.dart @@ -3,9 +3,7 @@ import '../git.dart'; import '../logger.dart'; import '../provider.dart'; -Future installDepotTools({ - required Scope scope, -}) async { +Future installDepotTools({required Scope scope}) async { final log = PuroLogger.of(scope); final config = PuroConfig.of(scope); final git = GitClient.of(scope); diff --git a/puro/lib/src/engine/prepare.dart b/puro/lib/src/engine/prepare.dart index d943986..85561a9 100644 --- a/puro/lib/src/engine/prepare.dart +++ b/puro/lib/src/engine/prepare.dart @@ -12,9 +12,7 @@ import '../provider.dart'; import 'depot_tools.dart'; import 'worker.dart'; -Future prepareEngineSystemDeps({ - required Scope scope, -}) async { +Future prepareEngineSystemDeps({required Scope scope}) async { await installDepotTools(scope: scope); if (Platform.isLinux) { @@ -55,10 +53,7 @@ Future prepareEngine({ final sharedRepository = config.sharedEngineDir; if (forkRemoteUrl != null || - !await git.checkCommitExists( - repository: sharedRepository, - commit: ref, - )) { + !await git.checkCommitExists(repository: sharedRepository, commit: ref)) { await fetchOrCloneShared( scope: scope, repository: sharedRepository, @@ -87,14 +82,12 @@ Future prepareEngine({ .childDirectory('objects') .childDirectory('info') .childFile('alternates'); - final sharedObjects = - sharedRepository.childDirectory('.git').childDirectory('objects'); + final sharedObjects = sharedRepository + .childDirectory('.git') + .childDirectory('objects'); alternatesFile.writeAsStringSync('${sharedObjects.path}\n'); await git.syncRemotes(repository: repository, remotes: remotes); - await git.checkout( - repository: repository, - ref: ref, - ); + await git.checkout(repository: repository, ref: ref); }); // This is Python, not JSON. @@ -130,21 +123,19 @@ cache_dir = ${jsonEncode(config.sharedGClientDir.path)} final stdoutFuture = proc.stdout .transform(utf8.decoder) .transform(const LineSplitter()) - .listen( - (line) { - log.d('gclient: $line'); - logSink.writeln(line); - }, - ).asFuture(); + .listen((line) { + log.d('gclient: $line'); + logSink.writeln(line); + }) + .asFuture(); final stderrFuture = proc.stderr .transform(utf8.decoder) .transform(const LineSplitter()) - .listen( - (line) { - log.d('(E) gclient: $line'); - logSink.writeln(line); - }, - ).asFuture(); + .listen((line) { + log.d('(E) gclient: $line'); + logSink.writeln(line); + }) + .asFuture(); final exitCode = await proc.exitCode; await stdoutFuture; diff --git a/puro/lib/src/engine/visual_studio.dart b/puro/lib/src/engine/visual_studio.dart index 56f0704..8a0b1a1 100644 --- a/puro/lib/src/engine/visual_studio.dart +++ b/puro/lib/src/engine/visual_studio.dart @@ -13,9 +13,7 @@ import '../provider.dart'; /// /// Mostly borrowed from https://github.com/flutter/flutter/blob/master/packages/flutter_tools/lib/src/windows/visual_studio.dart class VisualStudio { - VisualStudio({ - required this.scope, - }); + VisualStudio({required this.scope}); final Scope scope; late final config = PuroConfig.of(scope); @@ -23,8 +21,9 @@ class VisualStudio { late final fileSystem = config.fileSystem; /// Matches the description property from the vswhere.exe JSON output. - final RegExp _vswhereDescriptionProperty = - RegExp(r'\s*"description"\s*:\s*".*"\s*,?'); + final RegExp _vswhereDescriptionProperty = RegExp( + r'\s*"description"\s*:\s*".*"\s*,?', + ); /// True if Visual Studio installation was found. /// @@ -75,7 +74,7 @@ class VisualStudio { if (_bestVisualStudioDetails == null) { return false; } - return _bestVisualStudioDetails!.isComplete ?? true; + return _bestVisualStudioDetails.isComplete ?? true; } /// True if Visual Studio is launchable. @@ -85,7 +84,7 @@ class VisualStudio { if (_bestVisualStudioDetails == null) { return false; } - return _bestVisualStudioDetails!.isLaunchable ?? true; + return _bestVisualStudioDetails.isLaunchable ?? true; } /// True if the Visual Studio installation is a pre-release version. @@ -107,8 +106,9 @@ class VisualStudio { if (sdkLocation == null) { return null; } - final Directory sdkIncludeDirectory = - config.fileSystem.directory(sdkLocation).childDirectory('Include'); + final Directory sdkIncludeDirectory = config.fileSystem + .directory(sdkLocation) + .childDirectory('Include'); if (!sdkIncludeDirectory.existsSync()) { return null; } @@ -119,8 +119,9 @@ class VisualStudio { if (versionEntry.basename.startsWith('10.')) { // Version only handles 3 components; strip off the '10.' to leave three // components, since they all start with that. - final Version? version = - Version.parse(versionEntry.basename.substring(3)); + final Version? version = Version.parse( + versionEntry.basename.substring(3), + ); if (highestVersion == null || (version != null && version > highestVersion)) { highestVersion = version; @@ -300,23 +301,21 @@ class VisualStudio { '-latest', ]; - final whereResult = runProcessSync( - scope, - _vswherePath, - [ - ...defaultArguments, - ...?additionalArguments, - ...requirementArguments, - ], - stdoutEncoding: const Utf8Codec(allowMalformed: true), - ); + final whereResult = runProcessSync(scope, _vswherePath, [ + ...defaultArguments, + ...?additionalArguments, + ...requirementArguments, + ], stdoutEncoding: const Utf8Codec(allowMalformed: true)); if (whereResult.exitCode == 0) { - final List>? installations = - _tryDecodeVswhereJson(whereResult.stdout as String); + final List>? installations = _tryDecodeVswhereJson( + whereResult.stdout as String, + ); if (installations != null && installations.isNotEmpty) { return VswhereDetails.fromJson( - validateRequirements, installations[0]); + validateRequirements, + installations[0], + ); } } } on ArgumentError { @@ -378,14 +377,12 @@ class VisualStudio { for (final bool checkForPrerelease in [false, true]) { for (final String requiredWorkload in _requiredWorkloads) { final VswhereDetails? result = _visualStudioDetails( - validateRequirements: true, - additionalArguments: checkForPrerelease - ? [ - ...minimumVersionArguments, - _vswherePrereleaseArgument - ] - : minimumVersionArguments, - requiredWorkload: requiredWorkload); + validateRequirements: true, + additionalArguments: checkForPrerelease + ? [...minimumVersionArguments, _vswherePrereleaseArgument] + : minimumVersionArguments, + requiredWorkload: requiredWorkload, + ); if (result != null) { return result; @@ -396,7 +393,8 @@ class VisualStudio { // An installation that satisfies requirements could not be found. // Fallback to the latest Visual Studio installation. return _visualStudioDetails( - additionalArguments: [_vswherePrereleaseArgument, '-all']); + additionalArguments: [_vswherePrereleaseArgument, '-all'], + ); }(); /// Returns the installation location of the Windows 10 SDKs, or null if the @@ -441,8 +439,9 @@ class VisualStudio { } // Version only handles 3 components; strip off the '10.' to leave three // components, since they all start with that. - final Version? version = - Version.parse(versionEntry.basename.substring(3)); + final Version? version = Version.parse( + versionEntry.basename.substring(3), + ); if (highestVersion == null || (version != null && version > highestVersion)) { highestVersion = version; @@ -471,7 +470,9 @@ class VswhereDetails { /// Create a `VswhereDetails` from the JSON output of vswhere.exe. factory VswhereDetails.fromJson( - bool meetsRequirements, Map details) { + bool meetsRequirements, + Map details, + ) { final Map? catalog = details['catalog'] as Map?; @@ -489,8 +490,9 @@ class VswhereDetails { // Below are strings that are used only for display purposes and are allowed to // contain replacement characters. displayName: details['displayName'] as String?, - catalogDisplayVersion: - catalog == null ? null : catalog['productDisplayVersion'] as String?, + catalogDisplayVersion: catalog == null + ? null + : catalog['productDisplayVersion'] as String?, ); } diff --git a/puro/lib/src/engine/worker.dart b/puro/lib/src/engine/worker.dart index c9f063e..24e8173 100644 --- a/puro/lib/src/engine/worker.dart +++ b/puro/lib/src/engine/worker.dart @@ -39,16 +39,14 @@ Future installLinuxWorkerPackages({required Scope scope}) async { CommandMessage( 'Try running `sudo apt install ${tryApt.join(' ')}`', type: CompletionType.info, - ) + ), ]); } } final visualStudioProvider = Provider((scope) => VisualStudio(scope: scope)); -Future ensureVisualStudioInstalled({ - required Scope scope, -}) async { +Future ensureVisualStudioInstalled({required Scope scope}) async { final log = PuroLogger.of(scope); final vs = scope.read(visualStudioProvider); @@ -116,10 +114,12 @@ Future ensureWindowsDebuggerInstalled({required Scope scope}) async { final config = PuroConfig.of(scope); final log = PuroLogger.of(scope); - final win10sdkPath = - config.fileSystem.directory(vs.getWindows10SdkLocation()!); - final debuggersDir = - win10sdkPath.childDirectory('Debuggers').childDirectory('x86'); + final win10sdkPath = config.fileSystem.directory( + vs.getWindows10SdkLocation()!, + ); + final debuggersDir = win10sdkPath + .childDirectory('Debuggers') + .childDirectory('x86'); final dbghelpFile = debuggersDir.childFile('dbghelp.dll'); log.d('dbghelpFile: $dbghelpFile'); await ProgressNode.of(scope).wrap((scope, node) async { @@ -139,7 +139,7 @@ Future ensureWindowsDebuggerInstalled({required Scope scope}) async { int compareVersions(String a, String b) { final x = a.split('.').map(int.parse).toList(); final y = b.split('.').map(int.parse).toList(); - for (var i = 0;; i++) { + for (var i = 0; ; i++) { if (x.length <= i) { if (y.length <= i) { return 0; @@ -155,18 +155,21 @@ Future ensureWindowsDebuggerInstalled({required Scope scope}) async { final dependencyList = (jsonDecode(result.stdout as String) as List).toList(); - dependencyList.removeWhere((dynamic e) => - e['Version'] == null || - e['DisplayName'] == null || - !(e['DisplayName'] as String) - .startsWith('Windows Software Development Kit - Windows 10.')); + dependencyList.removeWhere( + (dynamic e) => + e['Version'] == null || + e['DisplayName'] == null || + !(e['DisplayName'] as String).startsWith( + 'Windows Software Development Kit - Windows 10.', + ), + ); if (dependencyList.isNotEmpty) { final dynamic dependency = dependencyList.reduce( (dynamic a, dynamic b) => compareVersions(a['Version'] as String, b['Version'] as String) > - 0 - ? a - : b, + 0 + ? a + : b, ); final dependencyKey = dependency['PSChildName'] as String; log.d('dependencyKey: $dependencyKey'); @@ -180,16 +183,12 @@ Future ensureWindowsDebuggerInstalled({required Scope scope}) async { installerFile = depDirectory.childFile('sdksetup.exe'); } if (installerFile.existsSync()) { - final result = await runProcess( - scope, - installerFile.path, - [ - '/features', - 'OptionId.WindowsDesktopDebuggers', - '/q', - '/norestart' - ], - ); + final result = await runProcess(scope, installerFile.path, [ + '/features', + 'OptionId.WindowsDesktopDebuggers', + '/q', + '/norestart', + ]); if (result.exitCode == 0) return; } } @@ -242,12 +241,9 @@ Future ensureWindowsPythonInstalled({required Scope scope}) async { if (parent.basename == 'WindowsApps' && parent.parent.basename == 'Microsoft') { // Double check that the executable is indeed useless before deleting. - final result = await runProcess( - scope, - program.path, - ['-V'], - runInShell: true, - ); + final result = await runProcess(scope, program.path, [ + '-V', + ], runInShell: true); if (result.exitCode == 9009) { program.deleteSync(); pythonPrograms.removeAt(i); @@ -257,13 +253,10 @@ Future ensureWindowsPythonInstalled({required Scope scope}) async { } if (pythonPrograms.length > 1) { CommandMessage.format( - (format) => 'Multiple installations of python found in your PATH\n' + (format) => + 'Multiple installations of python found in your PATH\n' 'If engine builds fail, try removing all but one:\n' - '${pythonPrograms.map((e) => '${format.color( - '*', - bold: true, - foregroundColor: Ansi8BitColor.red, - )} ${e.path}').join('\n')}', + '${pythonPrograms.map((e) => '${format.color('*', bold: true, foregroundColor: Ansi8BitColor.red)} ${e.path}').join('\n')}', type: CompletionType.alert, ).queue(scope); } else if (pythonPrograms.isEmpty) { @@ -275,12 +268,9 @@ Future ensureWindowsPythonInstalled({required Scope scope}) async { ), ]); } - final python3Result = await runProcess( - scope, - 'python', - ['-V'], - runInShell: true, - ); + final python3Result = await runProcess(scope, 'python', [ + '-V', + ], runInShell: true); if (python3Result.exitCode != 0) { throw CommandError( '`python -V` did not pass the vibe check (exited with code ${python3Result.exitCode})', diff --git a/puro/lib/src/env/create.dart b/puro/lib/src/env/create.dart index 80f6a9b..84293ee 100644 --- a/puro/lib/src/env/create.dart +++ b/puro/lib/src/env/create.dart @@ -19,10 +19,7 @@ import 'flutter_tool.dart'; import 'version.dart'; class EnvCreateResult extends CommandResult { - EnvCreateResult({ - required this.success, - required this.environment, - }); + EnvCreateResult({required this.success, required this.environment}); @override final bool success; @@ -30,8 +27,8 @@ class EnvCreateResult extends CommandResult { @override CommandMessage get message => CommandMessage( - 'Created new environment at `${environment.flutterDir.path}`', - ); + 'Created new environment at `${environment.flutterDir.path}`', + ); } /// Updates the engine version file, to replicate the functionality of @@ -65,12 +62,16 @@ Future updateEngineVersionFile({ ); } commit = await git.mergeBase( - repository: flutterConfig.sdkDir, - ref1: 'HEAD', - ref2: 'upstream/master'); + repository: flutterConfig.sdkDir, + ref1: 'HEAD', + ref2: 'upstream/master', + ); } else { commit = await git.mergeBase( - repository: flutterConfig.sdkDir, ref1: 'HEAD', ref2: 'origin/master'); + repository: flutterConfig.sdkDir, + ref1: 'HEAD', + ref2: 'origin/master', + ); } flutterConfig.engineVersionFile.writeAsStringSync('$commit\n'); @@ -235,10 +236,7 @@ Future createEnvironment({ if (engineVersion == null) { return; } - await downloadSharedEngine( - scope: scope, - engineCommit: engineVersion, - ); + await downloadSharedEngine(scope: scope, engineCommit: engineVersion); cacheEngineTime = clock.now(); }, // The user probably already has flutter cached so cloning forks will be @@ -257,17 +255,15 @@ Future createEnvironment({ ); // Replace flutter/dart with shims - await installEnvShims( - scope: scope, - environment: environment, - ); + await installEnvShims(scope: scope, environment: environment); final cloneTime = clock.now(); await engineTask; if (cacheEngineTime != null) { - final wouldveTaken = (cloneTime.difference(startTime)) + + final wouldveTaken = + (cloneTime.difference(startTime)) + (cacheEngineTime!.difference(startTime)); final took = clock.now().difference(startTime); log.v( @@ -280,15 +276,9 @@ Future createEnvironment({ await updateDefaultEnvSymlink(scope: scope); // Set up engine and compile tool - await setUpFlutterTool( - scope: scope, - environment: environment, - ); + await setUpFlutterTool(scope: scope, environment: environment); - return EnvCreateResult( - success: true, - environment: environment, - ); + return EnvCreateResult(success: true, environment: environment); } /// Clones or fetches from a remote, putting it in a shared repository. @@ -360,8 +350,9 @@ Future cloneFlutterWithSharedRefs({ .childDirectory('objects') .childDirectory('info') .childFile('alternates'); - final sharedObjects = - sharedRepository.childDirectory('.git').childDirectory('objects'); + final sharedObjects = sharedRepository + .childDirectory('.git') + .childDirectory('objects'); alternatesFile.writeAsStringSync('${sharedObjects.path}\n'); await git.syncRemotes(repository: repository, remotes: remotes); @@ -416,11 +407,7 @@ Future cloneFlutterWithSharedRefs({ node.description = 'Checking out $forkRef'; await guardCheckout(() async { - await git.checkout( - repository: repository, - ref: forkRef, - force: force, - ); + await git.checkout(repository: repository, ref: forkRef, force: force); }); }); @@ -445,10 +432,7 @@ Future cloneFlutterWithSharedRefs({ node.description = 'Checking out $flutterVersion'; - await git.fetch( - repository: repository, - all: true, - ); + await git.fetch(repository: repository, all: true); final branch = flutterVersion.branch; if (branch != null) { diff --git a/puro/lib/src/env/dart.dart b/puro/lib/src/env/dart.dart index 62d8806..f08f1dd 100644 --- a/puro/lib/src/env/dart.dart +++ b/puro/lib/src/env/dart.dart @@ -16,11 +16,7 @@ import '../progress.dart'; import '../provider.dart'; import 'engine.dart'; -enum DartChannel { - stable, - beta, - dev, -} +enum DartChannel { stable, beta, dev } enum DartOS { windows, @@ -30,10 +26,10 @@ enum DartOS { static final DartOS current = Platform.isWindows ? DartOS.windows : Platform.isMacOS - ? DartOS.macOS - : Platform.isLinux - ? DartOS.linux - : throw UnsupportedError('Unsupported platform'); + ? DartOS.macOS + : Platform.isLinux + ? DartOS.linux + : throw UnsupportedError('Unsupported platform'); } enum DartArch { @@ -43,8 +39,9 @@ enum DartArch { arm64, riscv64; - static final DartArch current = DartArch.values - .singleWhere((e) => '${ffi.Abi.current()}'.endsWith('_${e.name}')); + static final DartArch current = DartArch.values.singleWhere( + (e) => '${ffi.Abi.current()}'.endsWith('_${e.name}'), + ); } class DartRelease { @@ -56,12 +53,12 @@ class DartRelease { final Version version; Uri get downloadUrl => Uri.parse( - 'https://storage.googleapis.com/dart-archive/channels/${channel.name}/release/$version/sdk/dartsdk-${os.name.toLowerCase()}-${arch.name}-release.zip', - ); + 'https://storage.googleapis.com/dart-archive/channels/${channel.name}/release/$version/sdk/dartsdk-${os.name.toLowerCase()}-${arch.name}-release.zip', + ); Uri get versionUrl => Uri.parse( - 'https://storage.googleapis.com/storage/v1/b/dart-archive/o/channels%2F${channel.name}%2Frelease%2F$version%2FVERSION?alt=media', - ); + 'https://storage.googleapis.com/storage/v1/b/dart-archive/o/channels%2F${channel.name}%2Frelease%2F$version%2FVERSION?alt=media', + ); String get name => '${channel.name}-$version-${os.name}-${arch.name}'; } @@ -70,22 +67,21 @@ class DartReleases { DartReleases(this.releases); final Map> releases; Map toJson() => { - for (final channel in releases.keys) - channel.name: releases[channel]!.map((v) => '$v').toList(), - }; + for (final channel in releases.keys) + channel.name: releases[channel]!.map((v) => '$v').toList(), + }; factory DartReleases.fromJson(Map json) => DartReleases({ - for (final channel in json.keys) - DartChannel.values.singleWhere((e) => e.name == channel): - (json[channel] as List) - .map((v) => Version.parse(v as String)) - .toList(), - }); + for (final channel in json.keys) + DartChannel.values.singleWhere( + (e) => e.name == channel, + ): (json[channel] as List) + .map((v) => Version.parse(v as String)) + .toList(), + }); } /// Fetches all of the available Dart releases. -Future fetchDartReleases({ - required Scope scope, -}) async { +Future fetchDartReleases({required Scope scope}) async { final config = PuroConfig.of(scope); return ProgressNode.of(scope).wrap((scope, node) async { final releases = >{}; @@ -101,7 +97,8 @@ Future fetchDartReleases({ releases[channel] = []; for (final prefix in data['prefixes'] as List) { final version = tryParseVersion( - (prefix as String).split('/').lastWhere((e) => e.isNotEmpty)); + (prefix as String).split('/').lastWhere((e) => e.isNotEmpty), + ); if (version == null) continue; releases[channel]!.add(version); } @@ -135,35 +132,31 @@ Future getCachedDartReleases({ } /// Gets all of the available Dart releases, checking the cache first. -Future getDartReleases({ - required Scope scope, -}) async { +Future getDartReleases({required Scope scope}) async { final config = PuroConfig.of(scope); final log = PuroLogger.of(scope); final cachedReleasesStat = config.cachedDartReleasesJsonFile.statSync(); final hasCache = cachedReleasesStat.type == FileSystemEntityType.file; - var cacheIsFresh = hasCache && + var cacheIsFresh = + hasCache && clock.now().difference(cachedReleasesStat.modified).inHours < 1; // Don't read from the cache if it's stale. if (hasCache && cacheIsFresh) { DartReleases? cachedReleases; - await lockFile( - scope, - config.cachedDartReleasesJsonFile, - (handle) async { - final contents = await handle.readAllAsString(); - try { - cachedReleases = DartReleases.fromJson( - jsonDecode(contents) as Map); - } catch (exception, stackTrace) { - log.w('Error while parsing cached releases'); - log.w('$exception\n$stackTrace'); - cacheIsFresh = false; - } - }, - ); + await lockFile(scope, config.cachedDartReleasesJsonFile, (handle) async { + final contents = await handle.readAllAsString(); + try { + cachedReleases = DartReleases.fromJson( + jsonDecode(contents) as Map, + ); + } catch (exception, stackTrace) { + log.w('Error while parsing cached releases'); + log.w('$exception\n$stackTrace'); + cacheIsFresh = false; + } + }); if (cachedReleases != null) { return cachedReleases!; } @@ -192,9 +185,7 @@ Future downloadSharedDartRelease({ dartSdk.dartExecutable.path, ['--version'], throwOnFailure: true, - environment: { - 'PUB_CACHE': config.legacyPubCacheDir.path, - }, + environment: {'PUB_CACHE': config.legacyPubCacheDir.path}, ); }); } catch (exception) { @@ -209,8 +200,9 @@ Future downloadSharedDartRelease({ if (!config.sharedDartReleaseDir.existsSync()) { config.sharedDartReleaseDir.createSync(recursive: true); } - final zipFile = - config.sharedDartReleaseDir.childFile('${release.name}.zip'); + final zipFile = config.sharedDartReleaseDir.childFile( + '${release.name}.zip', + ); await downloadFile( scope: scope, diff --git a/puro/lib/src/env/default.dart b/puro/lib/src/env/default.dart index c02cc29..ec61fff 100644 --- a/puro/lib/src/env/default.dart +++ b/puro/lib/src/env/default.dart @@ -71,9 +71,7 @@ Future getProjectEnvOrDefault({ return env; } -Future getDefaultEnvName({ - required Scope scope, -}) async { +Future getDefaultEnvName({required Scope scope}) async { final prefs = await readGlobalPrefs(scope: scope); return prefs.hasDefaultEnvironment() ? prefs.defaultEnvironment : 'stable'; } @@ -89,10 +87,7 @@ Future setDefaultEnvName({ prefs.defaultEnvironment = envName; }, ); - await updateDefaultEnvSymlink( - scope: scope, - name: envName, - ); + await updateDefaultEnvSymlink(scope: scope, name: envName); } Future updateDefaultEnvSymlink({ diff --git a/puro/lib/src/env/delete.dart b/puro/lib/src/env/delete.dart index ef87930..7393280 100644 --- a/puro/lib/src/env/delete.dart +++ b/puro/lib/src/env/delete.dart @@ -16,18 +16,16 @@ Future ensureNoProjectsUsingEnv({ environment: environment, ); if (dotfiles.isNotEmpty) { - throw CommandError.list( - [ - CommandMessage( - 'Environment `${environment.name}` is currently used by the following ' - 'projects:\n${dotfiles.map((p) => '* ${config.shortenHome(p.parent.path)}').join('\n')}', - ), - CommandMessage( - 'Pass `-f` to ignore this warning', - type: CompletionType.info, - ), - ], - ); + throw CommandError.list([ + CommandMessage( + 'Environment `${environment.name}` is currently used by the following ' + 'projects:\n${dotfiles.map((p) => '* ${config.shortenHome(p.parent.path)}').join('\n')}', + ), + CommandMessage( + 'Pass `-f` to ignore this warning', + type: CompletionType.info, + ), + ]); } } @@ -60,25 +58,17 @@ Future deleteEnvironment({ // Try killing dart processes that might be preventing us from deleting the // environment. if (Platform.isWindows) { - await runProcess( - scope, - 'wmic', - [ - 'process', - 'where', - 'path="${env.flutter.cache.dartSdk.dartExecutable.resolveSymbolicLinksSync().replaceAll('\\', '\\\\')}"', - 'delete', - ], - ); + await runProcess(scope, 'wmic', [ + 'process', + 'where', + 'path="${env.flutter.cache.dartSdk.dartExecutable.resolveSymbolicLinksSync().replaceAll('\\', '\\\\')}"', + 'delete', + ]); } else { - final result = await runProcess( - scope, - 'pgrep', - [ - '-f', - env.flutter.cache.dartSdk.dartExecutable.path, - ], - ); + final result = await runProcess(scope, 'pgrep', [ + '-f', + env.flutter.cache.dartSdk.dartExecutable.path, + ]); final pids = (result.stdout as String).trim().split(RegExp('\\s+')); await runProcess(scope, 'kill', ['-9', ...pids]); } diff --git a/puro/lib/src/env/engine.dart b/puro/lib/src/env/engine.dart index fb55a28..99820b7 100644 --- a/puro/lib/src/env/engine.dart +++ b/puro/lib/src/env/engine.dart @@ -20,16 +20,9 @@ import '../terminal.dart'; import 'create.dart'; import 'gc.dart'; -enum EngineOS { - windows, - macOS, - linux, -} +enum EngineOS { windows, macOS, linux } -enum EngineArch { - x64, - arm64, -} +enum EngineArch { x64, arm64 } enum EngineBuildTarget { windowsX64('dart-sdk-windows-x64.zip', EngineOS.windows, EngineArch.x64), @@ -38,11 +31,7 @@ enum EngineBuildTarget { macosX64('dart-sdk-darwin-x64.zip', EngineOS.macOS, EngineArch.x64), macosArm64('dart-sdk-darwin-arm64.zip', EngineOS.macOS, EngineArch.arm64); - const EngineBuildTarget( - this.zipName, - this.os, - this.arch, - ); + const EngineBuildTarget(this.zipName, this.os, this.arch); final String zipName; final EngineOS os; @@ -76,9 +65,7 @@ enum EngineBuildTarget { throw AssertionError('Unsupported build target: $os $arch'); } - static Future query({ - required Scope scope, - }) async { + static Future query({required Scope scope}) async { final EngineOS os; final EngineArch arch; if (Platform.isWindows) { @@ -86,21 +73,17 @@ enum EngineBuildTarget { arch = EngineArch.x64; } else if (Platform.isMacOS) { os = EngineOS.macOS; - final sysctlResult = await runProcess( - scope, - 'sysctl', - ['-n', 'hw.optional.arm64'], - runInShell: true, - ); + final sysctlResult = await runProcess(scope, 'sysctl', [ + '-n', + 'hw.optional.arm64', + ], runInShell: true); final stdout = (sysctlResult.stdout as String).trim(); if (sysctlResult.exitCode != 0 || stdout == '0') { arch = EngineArch.x64; } else if (stdout == '1') { arch = EngineArch.arm64; } else { - throw AssertionError( - 'Unexpected result from sysctl: `$stdout`', - ); + throw AssertionError('Unexpected result from sysctl: `$stdout`'); } } else if (Platform.isLinux) { os = EngineOS.linux; @@ -125,8 +108,9 @@ enum EngineBuildTarget { return EngineBuildTarget.from(os, arch); } - static final Provider> provider = - Provider((scope) => query(scope: scope)); + static final Provider> provider = Provider( + (scope) => query(scope: scope), + ); } /// Unzips [zipFile] into [destination]. @@ -139,29 +123,19 @@ Future unzip({ if (Platform.isWindows) { final zip = await findProgramInPath(scope: scope, name: '7z'); if (zip.isNotEmpty) { - await runProcess( - scope, - zip.first.path, - [ - 'x', - '-y', - '-o${destination.path}', - zipFile.path, - ], - throwOnFailure: true, - ); + await runProcess(scope, zip.first.path, [ + 'x', + '-y', + '-o${destination.path}', + zipFile.path, + ], throwOnFailure: true); } else { - await runProcess( - scope, - 'powershell', - [ - 'Import-Module Microsoft.PowerShell.Archive; Expand-Archive', - zipFile.path, - '-DestinationPath', - destination.path, - ], - throwOnFailure: true, - ); + await runProcess(scope, 'powershell', [ + 'Import-Module Microsoft.PowerShell.Archive; Expand-Archive', + zipFile.path, + '-DestinationPath', + destination.path, + ], throwOnFailure: true); } } else if (Platform.isLinux || Platform.isMacOS) { const pm = LocalProcessManager(); @@ -179,13 +153,7 @@ Future unzip({ await runProcess( scope, 'unzip', - [ - '-o', - '-q', - zipFile.path, - '-d', - destination.path, - ], + ['-o', '-q', zipFile.path, '-d', destination.path], runInShell: true, throwOnFailure: true, ); @@ -213,9 +181,7 @@ Future downloadSharedEngine({ sharedCache.dartSdk.dartExecutable.path, ['--version'], throwOnFailure: true, - environment: { - 'PUB_CACHE': config.legacyPubCacheDir.path, - }, + environment: {'PUB_CACHE': config.legacyPubCacheDir.path}, ); }); } catch (exception) { @@ -246,7 +212,8 @@ Future downloadSharedEngine({ // of shared.sh or the git tree, but this is much simpler. if (e.statusCode == 404 && target == EngineBuildTarget.macosArm64) { final engineZipUrl = config.flutterStorageBaseUrl.append( - path: 'flutter_infra_release/flutter/$engineCommit/' + path: + 'flutter_infra_release/flutter/$engineCommit/' '${EngineBuildTarget.macosX64.zipName}', ); await downloadFile( @@ -290,12 +257,9 @@ Future getDartSDKVersion({ required Scope scope, required DartSdkConfig dartSdk, }) async { - final result = await runProcess( - scope, - dartSdk.dartExecutable.path, - ['--version'], - throwOnFailure: true, - ); + final result = await runProcess(scope, dartSdk.dartExecutable.path, [ + '--version', + ], throwOnFailure: true); final match = _dartSdkRegex.firstMatch(result.stdout as String); if (match == null) { throw AssertionError('Failed to parse `${result.stdout}`'); @@ -304,10 +268,7 @@ Future getDartSDKVersion({ } /// These files shouldn't be shared between flutter installs. -const cacheBlacklist = { - 'flutter_version_check.stamp', - 'flutter.version.json', -}; +const cacheBlacklist = {'flutter_version_check.stamp', 'flutter.version.json'}; extension EnvPrefsModelExtension on PuroEnvPrefsModel { bool get isPatched => hasPatched() && patched; @@ -323,17 +284,16 @@ Future syncFlutterCache({ final log = PuroLogger.of(scope); final config = PuroConfig.of(scope); final fs = config.fileSystem; - final engineVersion = - await getEngineVersion(scope: scope, flutterConfig: environment.flutter); + final engineVersion = await getEngineVersion( + scope: scope, + flutterConfig: environment.flutter, + ); if (engineVersion == null) { return; } environmentPrefs ??= await environment.readPrefs(scope: scope); final sharedCacheDir = config - .getFlutterCache( - engineVersion, - patched: environmentPrefs.isPatched, - ) + .getFlutterCache(engineVersion, patched: environmentPrefs.isPatched) .cacheDir; if (!sharedCacheDir.existsSync()) { return; @@ -363,8 +323,10 @@ Future syncFlutterCache({ // Delete the link if it doesn't point to the file we want. final link = fs.link(file.path); if (link.targetSync() != sharedFile.path) { - log.d('Deleting ${file.basename} symlink because it points to ' - '`${link.targetSync()}` instead of `${sharedFile.path}`'); + log.d( + 'Deleting ${file.basename} symlink because it points to ' + '`${link.targetSync()}` instead of `${sharedFile.path}`', + ); link.deleteSync(); } continue; @@ -373,8 +335,10 @@ Future syncFlutterCache({ if (fs.existsSync(sharedPath)) { // Delete local copy and link to shared copy, perhaps we could // merge them instead? - log.d('Deleting ${file.basename} because it already exists in the ' - 'shared cache'); + log.d( + 'Deleting ${file.basename} because it already exists in the ' + 'shared cache', + ); file.deleteSync(recursive: true); } else { // Move it to the shared cache. @@ -406,9 +370,6 @@ Future trySyncFlutterCache({ required EnvConfig environment, }) async { await runOptional(scope, 'Syncing flutter cache', () async { - await syncFlutterCache( - scope: scope, - environment: environment, - ); + await syncFlutterCache(scope: scope, environment: environment); }); } diff --git a/puro/lib/src/env/env_shims.dart b/puro/lib/src/env/env_shims.dart index 8ebcc2b..bd6378b 100644 --- a/puro/lib/src/env/env_shims.dart +++ b/puro/lib/src/env/env_shims.dart @@ -73,7 +73,8 @@ Future installEnvShims({ await writePassiveAtomic( scope: scope, file: flutterConfig.binDir.childFile('dart'), - content: '$bashShimHeader\n' + content: + '$bashShimHeader\n' 'export PURO_FLUTTER_BIN="\$(cd "\${PROG_NAME%/*}" ; pwd -P)"\n' 'PURO_BIN="\$PURO_FLUTTER_BIN/../../../../bin"\n' // Backing out of envs//flutter/bin '"\$PURO_BIN/puro" dart "\$@"\n', @@ -81,28 +82,26 @@ Future installEnvShims({ await writePassiveAtomic( scope: scope, file: flutterConfig.binDir.childFile('flutter'), - content: '$bashShimHeader\n' + content: + '$bashShimHeader\n' 'export PURO_FLUTTER_BIN="\$(cd "\${PROG_NAME%/*}" ; pwd -P)"\n' 'PURO_BIN="\$PURO_FLUTTER_BIN/../../../../bin"\n' // Backing out of envs//flutter/bin '"\$PURO_BIN/puro" flutter "\$@"\n', ); if (!Platform.isWindows) { - await runProcess( - scope, - 'chmod', - [ - '+x', - flutterConfig.binDir.childFile('dart').path, - flutterConfig.binDir.childFile('flutter').path, - ], - ); + await runProcess(scope, 'chmod', [ + '+x', + flutterConfig.binDir.childFile('dart').path, + flutterConfig.binDir.childFile('flutter').path, + ]); } await writePassiveAtomic( scope: scope, file: flutterConfig.binDir.childFile('dart.bat'), - content: '@echo off\n' + content: + '@echo off\n' 'SETLOCAL ENABLEDELAYEDEXPANSION\n' 'FOR %%i IN ("%~dp0.") DO SET PURO_FLUTTER_BIN=%%~fi\n' 'SET PURO_BIN=%PURO_FLUTTER_BIN%\\..\\..\\..\\..\\bin\n' @@ -111,7 +110,8 @@ Future installEnvShims({ await writePassiveAtomic( scope: scope, file: flutterConfig.binDir.childFile('flutter.bat'), - content: '@echo off\n' + content: + '@echo off\n' 'SETLOCAL ENABLEDELAYEDEXPANSION\n' 'FOR %%i IN ("%~dp0.") DO SET PURO_FLUTTER_BIN=%%~fi\n' 'SET PURO_BIN=%PURO_FLUTTER_BIN%\\..\\..\\..\\..\\bin\n' diff --git a/puro/lib/src/env/flutter_tool.dart b/puro/lib/src/env/flutter_tool.dart index de4896b..739b03b 100644 --- a/puro/lib/src/env/flutter_tool.dart +++ b/puro/lib/src/env/flutter_tool.dart @@ -86,8 +86,10 @@ Future setUpFlutterTool({ final log = PuroLogger.of(scope); final flutterConfig = environment.flutter; final flutterCache = flutterConfig.cache; - final desiredEngineVersion = - await getEngineVersion(scope: scope, flutterConfig: flutterConfig); + final desiredEngineVersion = await getEngineVersion( + scope: scope, + flutterConfig: flutterConfig, + ); if (config.project.parentPuroDotfile != null) { await registerDotfile( @@ -134,8 +136,9 @@ Future setUpFlutterTool({ }, ); - final commit = - await git.getCurrentCommitHash(repository: flutterConfig.sdkDir); + final commit = await git.getCurrentCommitHash( + repository: flutterConfig.sdkDir, + ); log.d('flutterCommit: $commit'); final pubspecLockFile = environment.flutter.flutterToolsPubspecLockFile; @@ -144,7 +147,8 @@ Future setUpFlutterTool({ var didUpdateTool = false; environmentPrefs ??= await environment.readPrefs(scope: scope); - final shouldPrecompile = !environmentPrefs!.hasPrecompileTool() || + final shouldPrecompile = + !environmentPrefs!.hasPrecompileTool() || environmentPrefs!.precompileTool; Future updateTool({required ToolQuirks toolQuirks}) async { @@ -153,7 +157,7 @@ Future setUpFlutterTool({ '${Platform.environment['PUB_ENVIRONMENT'] ?? ''}:flutter_install:puro'; var backoff = const Duration(seconds: 1); final rand = Random(); - for (var i = 0;; i++) { + for (var i = 0; ; i++) { node.description = 'Updating flutter tool'; final oldPubExecutable = flutterCache.dartSdk.oldPubExecutable; final usePubExecutable = oldPubExecutable.existsSync(); @@ -182,10 +186,11 @@ Future setUpFlutterTool({ throw AssertionError('pub upgrade failed after 10 attempts'); } else { // Exponential backoff with randomization - final randomizedBackoff = backoff + + final randomizedBackoff = + backoff + Duration( - milliseconds: - (backoff.inMilliseconds * rand.nextDouble() * 0.5).round(), + milliseconds: (backoff.inMilliseconds * rand.nextDouble() * 0.5) + .round(), ); backoff += backoff; log.w( @@ -248,9 +253,7 @@ Future setUpFlutterTool({ if (toolQuirks.disableMirrors) '--no-enable-mirrors', flutterConfig.flutterToolsScriptFile.path, ], - environment: { - 'PUB_CACHE': config.legacyPubCacheDir.path, - }, + environment: {'PUB_CACHE': config.legacyPubCacheDir.path}, throwOnFailure: true, ); }); @@ -271,9 +274,9 @@ Future setUpFlutterTool({ file: environment.updateLockFile, condition: () async => pubspecLockFile.existsSync() && - pubspecLockFile - .lastModifiedSync() - .isAfter(pubspecYamlFile.lastModifiedSync()), + pubspecLockFile.lastModifiedSync().isAfter( + pubspecYamlFile.lastModifiedSync(), + ), onFail: () async { log.v('Flutter tool out of date'); final toolQuirks = await getToolQuirks( diff --git a/puro/lib/src/env/gc.dart b/puro/lib/src/env/gc.dart index e716836..42a953d 100644 --- a/puro/lib/src/env/gc.dart +++ b/puro/lib/src/env/gc.dart @@ -36,8 +36,9 @@ Future collectGarbage({ usedCaches.add(engineVersion); } - final commit = - await git.tryGetCurrentCommitHash(repository: environment.flutterDir); + final commit = await git.tryGetCurrentCommitHash( + repository: environment.flutterDir, + ); if (commit != null) { usedCommits.add(commit); } @@ -47,7 +48,8 @@ Future collectGarbage({ for (final dir in sharedCacheDirs) { if (dir is! Directory || !isValidCommitHash(dir.basename) || - usedCaches.contains(dir.basename)) continue; + usedCaches.contains(dir.basename)) + continue; final config = FlutterCacheConfig(dir); final versionFile = config.engineVersionFile; if (versionFile.existsSync()) { @@ -63,7 +65,8 @@ Future collectGarbage({ for (final dir in flutterToolDirs) { if (dir is! Directory || !isValidCommitHash(dir.basename) || - usedCommits.contains(dir.basename)) continue; + usedCommits.contains(dir.basename)) + continue; final snapshotFile = dir.childFile('flutter_tool.snapshot'); if (snapshotFile.existsSync()) { unusedFlutterTools[dir] = snapshotFile.lastAccessedSync(); diff --git a/puro/lib/src/env/list.dart b/puro/lib/src/env/list.dart index 5f9a705..83c0c70 100644 --- a/puro/lib/src/env/list.dart +++ b/puro/lib/src/env/list.dart @@ -58,80 +58,69 @@ class ListEnvironmentResult extends CommandResult { @override CommandMessage get message { - return CommandMessage.format( - (format) { - if (results.isEmpty) { - return 'No environments, use `puro create` to create one'; + return CommandMessage.format((format) { + if (results.isEmpty) { + return 'No environments, use `puro create` to create one'; + } + final lines = >[]; + + for (final result in results) { + final name = result.environment.name; + final resultLines = []; + if (name == projectEnvironment) { + resultLines.add( + format.color( + '* $name', + foregroundColor: Ansi8BitColor.green, + bold: true, + ), + ); + } else if (name == globalEnvironment && projectEnvironment == null) { + resultLines.add( + format.color( + '~ $name', + foregroundColor: Ansi8BitColor.green, + bold: true, + ), + ); + } else if (name == globalEnvironment) { + resultLines.add('~ $name'); + } else { + resultLines.add(' $name'); } - final lines = >[]; - - for (final result in results) { - final name = result.environment.name; - final resultLines = []; - if (name == projectEnvironment) { - resultLines.add( - format.color( - '* $name', - foregroundColor: Ansi8BitColor.green, - bold: true, - ), - ); - } else if (name == globalEnvironment && projectEnvironment == null) { - resultLines.add( - format.color( - '~ $name', - foregroundColor: Ansi8BitColor.green, - bold: true, - ), - ); - } else if (name == globalEnvironment) { - resultLines.add('~ $name'); - } else { - resultLines.add(' $name'); + if (showProjects && result.projects.isNotEmpty) { + for (final project in result.projects) { + resultLines.add(' | ${config.shortenHome(project.path)}'); } - if (showProjects && result.projects.isNotEmpty) { - for (final project in result.projects) { - resultLines.add(' | ${config.shortenHome(project.path)}'); - } - } - lines.add(resultLines); } + lines.add(resultLines); + } - final linePadding = - lines.fold(0, (v, e) => max(v, stripAnsiEscapes(e[0]).length)); - - return [ - 'Environments:', - for (var i = 0; i < lines.length; i++) ...[ - padRightColored(lines[i][0], linePadding) + - format.color( - ' (${[ - if (results[i].environment.exists) - results[i].version ?? 'unknown' - else - 'not installed', - if (results[i].dartVersion != null && - results[i].showDartVersion) - 'Dart ${results[i].dartVersion}', - ].join(' / ')})', - foregroundColor: Ansi8BitColor.grey, - ), - ...lines[i].skip(1), - ], - '', - 'Use `puro create ` to create an environment, or `puro use ` to switch', - ].join('\n'); - }, - type: CompletionType.info, - ); + final linePadding = lines.fold( + 0, + (v, e) => max(v, stripAnsiEscapes(e[0]).length), + ); + + return [ + 'Environments:', + for (var i = 0; i < lines.length; i++) ...[ + padRightColored(lines[i][0], linePadding) + + format.color( + ' (${[if (results[i].environment.exists) results[i].version ?? 'unknown' else 'not installed', if (results[i].dartVersion != null && results[i].showDartVersion) 'Dart ${results[i].dartVersion}'].join(' / ')})', + foregroundColor: Ansi8BitColor.grey, + ), + ...lines[i].skip(1), + ], + '', + 'Use `puro create ` to create an environment, or `puro use ` to switch', + ].join('\n'); + }, type: CompletionType.info); } @override late final model = CommandResultModel( environmentList: EnvironmentListModel( - environments: [ - for (final info in results) info.toModel(), - ], + environments: [for (final info in results) info.toModel()], projectEnvironment: projectEnvironment, globalEnvironment: globalEnvironment, ), @@ -165,10 +154,18 @@ Future listEnvironments({ ? dartVersionFile.readAsStringSync().trim() : null; } - final projects = - (allDotfiles[environment.name] ?? []).map((e) => e.parent).toList(); - results.add(EnvironmentInfoResult( - environment, version, dartVersion, projects, showDartVersion)); + final projects = (allDotfiles[environment.name] ?? []) + .map((e) => e.parent) + .toList(); + results.add( + EnvironmentInfoResult( + environment, + version, + dartVersion, + projects, + showDartVersion, + ), + ); } if (config.envsDir.existsSync()) { @@ -188,10 +185,18 @@ Future listEnvironments({ final dartVersion = dartVersionFile.existsSync() ? dartVersionFile.readAsStringSync().trim() : null; - final projects = - (allDotfiles[environment.name] ?? []).map((e) => e.parent).toList(); - results.add(EnvironmentInfoResult( - environment, version, dartVersion, projects, showDartVersion)); + final projects = (allDotfiles[environment.name] ?? []) + .map((e) => e.parent) + .toList(); + results.add( + EnvironmentInfoResult( + environment, + version, + dartVersion, + projects, + showDartVersion, + ), + ); } } diff --git a/puro/lib/src/env/prepare.dart b/puro/lib/src/env/prepare.dart index 7b3ca2c..f442a8c 100644 --- a/puro/lib/src/env/prepare.dart +++ b/puro/lib/src/env/prepare.dart @@ -33,7 +33,9 @@ List sortPreparePlatforms(Iterable platforms) { List defaultPreparePlatforms() { final platforms = {'android', 'web'}; if (Platform.isMacOS) { - platforms..add('ios')..add('macos'); + platforms + ..add('ios') + ..add('macos'); } if (Platform.isLinux) { platforms.add('linux'); @@ -51,6 +53,9 @@ Future prepareEnvironment({ bool allPlatforms = false, bool force = false, }) async { + final effectivePlatforms = platforms == null + ? const [] + : sortPreparePlatforms(platforms); final args = ['precache']; if (force) { args.add('--force'); @@ -58,7 +63,7 @@ Future prepareEnvironment({ if (allPlatforms) { args.add('--all-platforms'); } - for (final platform in platforms ?? const []) { + for (final platform in effectivePlatforms) { args.add('--$platform'); } final exitCode = await runFlutterCommand( diff --git a/puro/lib/src/env/releases.dart b/puro/lib/src/env/releases.dart index 6fddd8d..08b2969 100644 --- a/puro/lib/src/env/releases.dart +++ b/puro/lib/src/env/releases.dart @@ -103,7 +103,8 @@ Future findFrameworkRelease({ final cachedReleasesStat = config.cachedReleasesJsonFile.statSync(); final hasCache = cachedReleasesStat.type == FileSystemEntityType.file; - var cacheIsFresh = hasCache && + var cacheIsFresh = + hasCache && clock.now().difference(cachedReleasesStat.modified).inHours < 1; final isChannelOnly = channel != null && version == null; @@ -111,21 +112,17 @@ Future findFrameworkRelease({ // release. if (hasCache && (!isChannelOnly || cacheIsFresh)) { FlutterReleasesModel? cachedReleases; - await lockFile( - scope, - config.cachedReleasesJsonFile, - (handle) async { - final contents = await handle.readAllAsString(); - try { - cachedReleases = FlutterReleasesModel.create() - ..mergeFromProto3Json(jsonDecode(contents)); - } catch (exception, stackTrace) { - log.w('Error while parsing cached releases'); - log.w('$exception\n$stackTrace'); - cacheIsFresh = false; - } - }, - ); + await lockFile(scope, config.cachedReleasesJsonFile, (handle) async { + final contents = await handle.readAllAsString(); + try { + cachedReleases = FlutterReleasesModel.create() + ..mergeFromProto3Json(jsonDecode(contents)); + } catch (exception, stackTrace) { + log.w('Error while parsing cached releases'); + log.w('$exception\n$stackTrace'); + cacheIsFresh = false; + } + }); if (cachedReleases != null) { final foundRelease = searchFlutterVersions( releases: cachedReleases!, diff --git a/puro/lib/src/env/rename.dart b/puro/lib/src/env/rename.dart index 0e0bf26..9224c1d 100644 --- a/puro/lib/src/env/rename.dart +++ b/puro/lib/src/env/rename.dart @@ -25,23 +25,16 @@ Future renameEnvironment({ final newEnv = config.getEnv(newName); if (newEnv.exists) { - throw CommandError( - 'Environment `$newName` already exists', - ); + throw CommandError('Environment `$newName` already exists'); } else if (env.name == newEnv.name) { - throw CommandError( - 'Environment `$name` is already named `$newName`', - ); + throw CommandError('Environment `$name` is already named `$newName`'); } else if (isPseudoEnvName(newName)) { throw CommandError( 'Environment `$newName` is already pinned to a version, use `puro create $newName` to create it', ); } - final dotfiles = await getDotfilesUsingEnv( - scope: scope, - environment: env, - ); + final dotfiles = await getDotfilesUsingEnv(scope: scope, environment: env); if (env.updateLockFile.existsSync()) { await env.updateLockFile.delete(); @@ -73,8 +66,9 @@ Future renameEnvironment({ final model = PuroDotfileModel.create(); model.mergeFromProto3Json(data); model.env = newName; - dotfile - .writeAsStringSync(prettyJsonEncoder.convert(model.toProto3Json())); + dotfile.writeAsStringSync( + prettyJsonEncoder.convert(model.toProto3Json()), + ); } }); diff --git a/puro/lib/src/env/upgrade.dart b/puro/lib/src/env/upgrade.dart index 3ac1ed8..ccc470d 100644 --- a/puro/lib/src/env/upgrade.dart +++ b/puro/lib/src/env/upgrade.dart @@ -33,13 +33,13 @@ class EnvUpgradeResult extends CommandResult { @override CommandMessage get message => CommandMessage.format( - (format) => from.commit == to.commit - ? toolInfo.didUpdateTool || toolInfo.didUpdateEngine - ? 'Finished installation of $to in environment `${environment.name}`' - : 'Environment `${environment.name}` is already up to date' - : '${downgrade ? 'Downgraded' : 'Upgraded'} environment `${environment.name}`\n' - '${from.toString(format)} => ${to.toString(format)}', - ); + (format) => from.commit == to.commit + ? toolInfo.didUpdateTool || toolInfo.didUpdateEngine + ? 'Finished installation of $to in environment `${environment.name}`' + : 'Environment `${environment.name}` is already up to date' + : '${downgrade ? 'Downgraded' : 'Upgraded'} environment `${environment.name}`\n' + '${from.toString(format)} => ${to.toString(format)}', + ); @override late final model = CommandResultModel( @@ -106,14 +106,9 @@ Future upgradeEnvironment({ ); } if (await git.hasUncomittedChanges(repository: repository)) { - throw CommandError( - "Can't upgrade fork with uncomitted changes", - ); + throw CommandError("Can't upgrade fork with uncomitted changes"); } - await git.pull( - repository: repository, - all: true, - ); + await git.pull(repository: repository, all: true); final switchBranch = toVersion.branch != null && branch != toVersion.branch; if (switchBranch) { @@ -152,10 +147,7 @@ Future upgradeEnvironment({ } // Replace flutter/dart with shims - await installEnvShims( - scope: scope, - environment: environment, - ); + await installEnvShims(scope: scope, environment: environment); final toolInfo = await setUpFlutterTool( scope: scope, diff --git a/puro/lib/src/env/version.dart b/puro/lib/src/env/version.dart index 985fe3a..3890eb6 100644 --- a/puro/lib/src/env/version.dart +++ b/puro/lib/src/env/version.dart @@ -160,14 +160,9 @@ class FlutterVersion { arg: version, ); if (commit != null) { - return FlutterVersion( - commit: commit, - version: parsedVersion, - ); + return FlutterVersion(commit: commit, version: parsedVersion); } - throw CommandError( - 'Could not find version $version', - ); + throw CommandError('Could not find version $version'); } final releaseVersion = tryParseVersion(release.version); if (releaseVersion == null) { @@ -192,10 +187,7 @@ class FlutterVersion { arg: 'tags/$version', ); if (result != null) { - return FlutterVersion( - commit: result, - tag: version, - ); + return FlutterVersion(commit: result, tag: version); } // Check if it's a commit @@ -232,10 +224,7 @@ class FlutterVersion { repository: sharedRepository, branch: 'origin/$version', ); - return FlutterVersion( - commit: result, - branch: isBranch ? version : null, - ); + return FlutterVersion(commit: result, branch: isBranch ? version : null); } // Check again after fetching @@ -313,9 +302,5 @@ Future getEnvironmentFlutterVersion({ version = tryParseVersion(versionFile.readAsStringSync().trim()); } final branch = await git.getBranch(repository: flutterConfig.sdkDir); - return FlutterVersion( - commit: commit, - version: version, - branch: branch, - ); + return FlutterVersion(commit: commit, version: version, branch: branch); } diff --git a/puro/lib/src/eval/context.dart b/puro/lib/src/eval/context.dart index 722d034..ee551e5 100644 --- a/puro/lib/src/eval/context.dart +++ b/puro/lib/src/eval/context.dart @@ -112,12 +112,7 @@ class EvalImport { @override String toString() { - return "import ${[ - "'$uri'", - if (as != null) 'as $as', - if (show.isNotEmpty) 'show ${show.join(', ')}', - if (hide.isNotEmpty) 'hide ${hide.join(', ')}', - ].join(' ')};"; + return "import ${["'$uri'", if (as != null) 'as $as', if (show.isNotEmpty) 'show ${show.join(', ')}', if (hide.isNotEmpty) 'hide ${hide.join(', ')}'].join(' ')};"; } @override @@ -133,11 +128,11 @@ class EvalImport { @override int get hashCode => Object.hash( - uri, - as, - Object.hashAllUnordered(show), - Object.hashAllUnordered(hide), - ); + uri, + as, + Object.hashAllUnordered(show), + Object.hashAllUnordered(hide), + ); } MapEntry parseEvalPackage(String package) { @@ -172,10 +167,7 @@ class EvalError implements Exception { } class EvalContext { - EvalContext({ - required this.scope, - required this.environment, - }); + EvalContext({required this.scope, required this.environment}); final Scope scope; final EnvConfig environment; @@ -218,20 +210,25 @@ class EvalContext { log.d('unitNode: ${unitNode.runtimeType}'); log.d(() => 'unitNode.directives: ${unitNode?.directives}'); - log.d(() => 'unitNode.declarations: ' - '${unitNode?.declarations.map((e) => e.runtimeType).join(', ')}'); + log.d( + () => + 'unitNode.declarations: ' + '${unitNode?.declarations.map((e) => e.runtimeType).join(', ')}', + ); // Always use unit if it contains top-level declarations, contains imports, // or contains a main function. if (unitNode != null && (unitNode.directives.isNotEmpty || - unitNode.declarations.any((e) => - e is ClassDeclaration || - e is MixinDeclaration || - e is ExtensionDeclaration || - e is EnumDeclaration || - e is TypeAlias || - (e is FunctionDeclaration && e.name.lexeme == 'main')))) { + unitNode.declarations.any( + (e) => + e is ClassDeclaration || + e is MixinDeclaration || + e is ExtensionDeclaration || + e is EnumDeclaration || + e is TypeAlias || + (e is FunctionDeclaration && e.name.lexeme == 'main'), + ))) { return unitParseResult; } @@ -239,16 +236,26 @@ class EvalContext { final expressionNode = expressionParseResult.node; log.d('expressionNode: ${expressionNode.runtimeType}'); - log.d('expressionParseResult.parseErrors: ' - '${expressionParseResult.parseErrors}'); - log.d('expressionParseResult.scanErrors: ' - '${expressionParseResult.scanErrors}'); - log.d('expressionParseResult.parseException: ' - '${expressionParseResult.parseException}'); - log.d('expressionParseResult.scanException: ' - '${expressionParseResult.scanException}'); - log.d('expressionParseResult.exhaustive: ' - '${expressionParseResult.exhaustive}'); + log.d( + 'expressionParseResult.parseErrors: ' + '${expressionParseResult.parseErrors}', + ); + log.d( + 'expressionParseResult.scanErrors: ' + '${expressionParseResult.scanErrors}', + ); + log.d( + 'expressionParseResult.parseException: ' + '${expressionParseResult.parseException}', + ); + log.d( + 'expressionParseResult.scanException: ' + '${expressionParseResult.scanException}', + ); + log.d( + 'expressionParseResult.exhaustive: ' + '${expressionParseResult.exhaustive}', + ); if (!expressionParseResult.hasError && expressionParseResult.exhaustive) { return expressionParseResult; diff --git a/puro/lib/src/eval/packages.dart b/puro/lib/src/eval/packages.dart index 87d4a81..3fec145 100644 --- a/puro/lib/src/eval/packages.dart +++ b/puro/lib/src/eval/packages.dart @@ -36,9 +36,13 @@ Future updateBootstrapPackages({ log.d('pubspecLockFile exists'); final existingPackages = {}; if (pubspecLockFile.existsSync()) { - final yamlData = loadYaml(pubspecLockFile.existsSync() - ? pubspecLockFile.readAsStringSync() - : '{}') as YamlMap; + final yamlData = + loadYaml( + pubspecLockFile.existsSync() + ? pubspecLockFile.readAsStringSync() + : '{}', + ) + as YamlMap; for (final package in (yamlData['packages'] as YamlMap).entries) { final name = package.key as String; final version = Version.parse(package.value['version'] as String); @@ -50,8 +54,9 @@ Future updateBootstrapPackages({ final hasPackage = existingPackages.containsKey(entry.key); if ((hasPackage == (entry.value == VersionConstraint.empty)) || (hasPackage && - !(entry.value ?? VersionConstraint.any) - .allows(existingPackages[entry.key]!))) { + !(entry.value ?? VersionConstraint.any).allows( + existingPackages[entry.key]!, + ))) { log.d( 'not satisfied: ${entry.key} must be ${entry.value} ' 'but is ${existingPackages[entry.key]}', diff --git a/puro/lib/src/eval/parse.dart b/puro/lib/src/eval/parse.dart index 3e21d4e..fed4cd8 100644 --- a/puro/lib/src/eval/parse.dart +++ b/puro/lib/src/eval/parse.dart @@ -85,11 +85,8 @@ ParseResult parseDart( final scanErrors = _ErrorListener(); final reader = CharSequenceReader(code); final featureSet = FeatureSet.latestLanguageVersion(); - final scanner = Scanner( - source, - reader, - scanErrors, - )..configureFeatures( + final scanner = Scanner(source, reader, scanErrors) + ..configureFeatures( featureSetForOverriding: featureSet, featureSet: featureSet, ); @@ -127,32 +124,28 @@ ParseResult parseDart( ParseResult parseDartExpression( String code, { bool async = false, -}) => - parseDart( - async ? '() async => $code' : code, - (parser) { - final expr = parser.parseExpression2(); - if (async && expr is FunctionExpression) { - return (expr.body as ExpressionFunctionBody).expression; - } - return expr; - }, - ); +}) => parseDart(async ? '() async => $code' : code, (parser) { + final expr = parser.parseExpression2(); + if (async && expr is FunctionExpression) { + return (expr.body as ExpressionFunctionBody).expression; + } + return expr; +}); -ParseResult parseDartCompilationUnit(String code) => parseDart( - code, - (parser) => parser.parseCompilationUnit2(), - ); +ParseResult parseDartCompilationUnit(String code) => + parseDart(code, (parser) => parser.parseCompilationUnit2()); ParseResult parseDartBlock(String code) => parseDart( - code, - (parser) => (parser.parseFunctionBody( - false, - ParserErrorCode.MISSING_FUNCTION_BODY, - false, - ) as BlockFunctionBody) + code, + (parser) => + (parser.parseFunctionBody( + false, + ParserErrorCode.MISSING_FUNCTION_BODY, + false, + ) + as BlockFunctionBody) .block, - ); +); class _ErrorListener implements AnalysisErrorListener { final errors = []; diff --git a/puro/lib/src/eval/worker.dart b/puro/lib/src/eval/worker.dart index 6175e05..ec3e7f9 100644 --- a/puro/lib/src/eval/worker.dart +++ b/puro/lib/src/eval/worker.dart @@ -144,14 +144,16 @@ class EvalWorker { // Connecting to the observatory requires a token which we can only get from // scanning stdout. - final stdoutLines = const LineSplitter() - .bind(const Utf8Decoder(allowMalformed: true).bind(process.stdout)); + final stdoutLines = const LineSplitter().bind( + const Utf8Decoder(allowMalformed: true).bind(process.stdout), + ); final serverUriCompleter = Completer(); final stdoutFuture = stdoutLines.listen((line) { if (!serverUriCompleter.isCompleted && line.startsWith('The Dart VM service is listening on')) { - serverUriCompleter - .complete(line.split(' ').last.replaceAll('http://', 'ws://')); + serverUriCompleter.complete( + line.split(' ').last.replaceAll('http://', 'ws://'), + ); } }).asFuture(); final stderrFuture = process.stderr.listen(stderr.add).asFuture(); @@ -196,8 +198,9 @@ class EvalWorker { mainFileHandle: mainFileHandle, vmService: vmService, isolateId: isolateId, - onExit: - stdoutFuture.then((_) => stderrFuture.then((_) => process.exitCode)), + onExit: stdoutFuture.then( + (_) => stderrFuture.then((_) => process.exitCode), + ), context: context, currentCode: initialCode, ); @@ -207,7 +210,8 @@ class EvalWorker { log.d(() => '_evaluate: ${jsonEncode(parseResult.code)}'); if (parseResult.code == currentCode.code && !context.needsPackageReload && - reloadSuccessful) return; + reloadSuccessful) + return; reloadSuccessful = false; evalFile.writeAsStringSync(parseResult.code); currentCode = parseResult; @@ -221,8 +225,9 @@ class EvalWorker { if (reloadResult.success == false) { final notices = reloadResult.json!['notices'] as List; - final dynamic reason = - notices.firstWhere((dynamic e) => e['type'] == 'ReasonForCancelling'); + final dynamic reason = notices.firstWhere( + (dynamic e) => e['type'] == 'ReasonForCancelling', + ); throw EvalError(message: reason['message'] as String); } diff --git a/puro/lib/src/extensions.dart b/puro/lib/src/extensions.dart index e6666aa..5f946ea 100644 --- a/puro/lib/src/extensions.dart +++ b/puro/lib/src/extensions.dart @@ -187,11 +187,7 @@ extension NumExtensions on num { bool plusSign = true, }) { precision ??= abs() > 0 && abs() < 1 ? 2 : 0; - return '${(this * 100).pretty( - precision: precision, - minusSign: minusSign, - plusSign: plusSign, - )}%'; + return '${(this * 100).pretty(precision: precision, minusSign: minusSign, plusSign: plusSign)}%'; } /// Returns a short formatted number using abbreviations. @@ -218,71 +214,27 @@ extension NumExtensions on num { } if (this == 0) { - return pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - ); + return pretty(precision: 0, plusSign: plusSign, minusSign: minusSign); } else if (this < 1) { - return pretty( - precision: 1, - plusSign: plusSign, - minusSign: minusSign, - ); + return pretty(precision: 1, plusSign: plusSign, minusSign: minusSign); } else if (this < 100) { - return pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - ); - } else if (this < 5000 /* 5K */) { - return '${(this / 1000).pretty( - precision: 1, - plusSign: plusSign, - minusSign: minusSign, - )}K'; - } else if (this < 500000 /* 500K */) { - return '${(this / 1000).pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - )}K'; - } else if (this < 5000000 /* 5M */) { - return '${(this / 1000000).pretty( - precision: 1, - plusSign: plusSign, - minusSign: minusSign, - )}M'; - } else if (this < 500000000 /* 500M */) { - return '${(this / 1000000).pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - )}M'; - } else if (this < 5000000000 /* 5B */) { - return '${(this / 1000000000).pretty( - precision: 1, - plusSign: plusSign, - minusSign: minusSign, - )}${metric ? 'G' : 'B'}'; - } else if (this < 500000000000 /* 500B */) { - return '${(this / 1000000000).pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - )}${metric ? 'G' : 'B'}'; - } else if (this < 5000000000000 /* 5T */) { - return '${(this / 1000000000000).pretty( - precision: 1, - plusSign: plusSign, - minusSign: minusSign, - )}T'; + return pretty(precision: 0, plusSign: plusSign, minusSign: minusSign); + } else if (this < 5000 /* 5K */ ) { + return '${(this / 1000).pretty(precision: 1, plusSign: plusSign, minusSign: minusSign)}K'; + } else if (this < 500000 /* 500K */ ) { + return '${(this / 1000).pretty(precision: 0, plusSign: plusSign, minusSign: minusSign)}K'; + } else if (this < 5000000 /* 5M */ ) { + return '${(this / 1000000).pretty(precision: 1, plusSign: plusSign, minusSign: minusSign)}M'; + } else if (this < 500000000 /* 500M */ ) { + return '${(this / 1000000).pretty(precision: 0, plusSign: plusSign, minusSign: minusSign)}M'; + } else if (this < 5000000000 /* 5B */ ) { + return '${(this / 1000000000).pretty(precision: 1, plusSign: plusSign, minusSign: minusSign)}${metric ? 'G' : 'B'}'; + } else if (this < 500000000000 /* 500B */ ) { + return '${(this / 1000000000).pretty(precision: 0, plusSign: plusSign, minusSign: minusSign)}${metric ? 'G' : 'B'}'; + } else if (this < 5000000000000 /* 5T */ ) { + return '${(this / 1000000000000).pretty(precision: 1, plusSign: plusSign, minusSign: minusSign)}T'; } else { - return '${(this / 1000000000000).pretty( - precision: 0, - plusSign: plusSign, - minusSign: minusSign, - )}T'; + return '${(this / 1000000000000).pretty(precision: 0, plusSign: plusSign, minusSign: minusSign)}T'; } } } @@ -299,10 +251,7 @@ extension DurationExtensions on Duration { 'year': 31556952.0, }; - String pretty({ - String before = 'before', - bool abbr = false, - }) { + String pretty({String before = 'before', bool abbr = false}) { if (before.isNotEmpty) before = ' $before'; var s = inMicroseconds / 1000000; if (s == double.infinity) return 'never'; diff --git a/puro/lib/src/file_lock.dart b/puro/lib/src/file_lock.dart index 6007868..805a792 100644 --- a/puro/lib/src/file_lock.dart +++ b/puro/lib/src/file_lock.dart @@ -42,8 +42,9 @@ Future lockFile( await mutex.acquire(); try { exclusive ??= mode != FileMode.read; - final fileLock = - exclusive ? FileLock.blockingExclusive : FileLock.blockingShared; + final fileLock = exclusive + ? FileLock.blockingExclusive + : FileLock.blockingShared; RandomAccessFile handle; try { handle = await file.open(mode: mode); @@ -82,14 +83,9 @@ Future readBytesAtomic({ required File file, bool background = false, }) async { - return await lockFile( - scope, - file, - (handle) async { - return handle.read(handle.lengthSync()); - }, - background: background, - ); + return await lockFile(scope, file, (handle) async { + return handle.read(handle.lengthSync()); + }, background: background); } /// Acquires an shared lock on a file before reading from it. @@ -192,7 +188,8 @@ Future compareFileBytesAtomic({ return lockFile(scope, file, (handle) async { if (prefix ? handle.lengthSync() < bytes.length - : handle.lengthSync() != bytes.length) return false; + : handle.lengthSync() != bytes.length) + return false; return _bytesEqual(await handle.read(bytes.length), bytes); }); } @@ -247,10 +244,7 @@ Future createLink({ required Link link, required String path, }) async { - return createLinks( - scope: scope, - paths: {link: path}, - ); + return createLinks(scope: scope, paths: {link: path}); } /// Creates multiple links, elevating to admin in case the user is on Windows @@ -279,17 +273,13 @@ Future createLinks({ '"${escapeCmdString(paths[link]!)}"', ], ]; - final startProc = 'Start-Process cmd -Wait -Verb runAs -ArgumentList ' + final startProc = + 'Start-Process cmd -Wait -Verb runAs -ArgumentList ' '${args.map(escapePowershellString).map((e) => '"$e"').join(',')}'; - await runProcess( - scope, - 'powershell', - [ - '-command', - startProc, - ], - throwOnFailure: true, - ); + await runProcess(scope, 'powershell', [ + '-command', + startProc, + ], throwOnFailure: true); for (final entry in paths.entries) { final linkTarget = entry.key.targetSync(); if (linkTarget != entry.value) { diff --git a/puro/lib/src/git.dart b/puro/lib/src/git.dart index 3975949..452d02a 100644 --- a/puro/lib/src/git.dart +++ b/puro/lib/src/git.dart @@ -27,9 +27,7 @@ enum GitCloneStep { } class GitClient { - GitClient({ - required this.scope, - }); + GitClient({required this.scope}); final Scope scope; late final _puroConfig = PuroConfig.of(scope); @@ -68,24 +66,22 @@ class GitClient { } else { stdout = const LineSplitter() .bind(systemEncoding.decoder.bind(process.stdout)) - .map( - (e) { - _log.d('git: $e'); - if (onStdout != null) onStdout(e); - return e; - }, - ).join('\n'); + .map((e) { + _log.d('git: $e'); + if (onStdout != null) onStdout(e); + return e; + }) + .join('\n'); } final stderr = const LineSplitter() .bind(systemEncoding.decoder.bind(process.stderr)) - .map( - (e) { - _log.d('git: $e'); - if (onStderr != null) onStderr(e); - return e; - }, - ).join('\n'); + .map((e) { + _log.d('git: $e'); + if (onStderr != null) onStderr(e); + return e; + }) + .join('\n'); final exitCode = await process.exitCode; @@ -93,12 +89,7 @@ class GitClient { _log.v('git failed with exit code $exitCode'); } - return ProcessResult( - process.pid, - exitCode, - await stdout, - await stderr, - ); + return ProcessResult(process.pid, exitCode, await stdout, await stderr); } void _ensureSuccess(ProcessResult result) { @@ -114,10 +105,7 @@ class GitClient { /// https://git-scm.com/docs/git-init Future init({required Directory repository}) async { - final result = await raw( - ['init'], - directory: repository, - ); + final result = await raw(['init'], directory: repository); _ensureSuccess(result); } @@ -153,12 +141,9 @@ class GitClient { if (percentIndex < 0) { continue; } - final percent = int.tryParse(line - .substring( - prefix.length, - percentIndex, - ) - .trimLeft()); + final percent = int.tryParse( + line.substring(prefix.length, percentIndex).trimLeft(), + ); if (percent == null) continue; onProgress(step, percent / 100); } @@ -199,17 +184,14 @@ class GitClient { bool force = false, String? newBranch, }) async { - final result = await raw( - [ - 'checkout', - if (detach) '--detach', - if (track) '--track', - if (force) '-f', - if (newBranch != null) ...['-b', newBranch], - if (ref != null) ref, - ], - directory: repository, - ); + final result = await raw([ + 'checkout', + if (detach) '--detach', + if (track) '--track', + if (force) '-f', + if (newBranch != null) ...['-b', newBranch], + if (ref != null) ref, + ], directory: repository); _ensureSuccess(result); } @@ -223,18 +205,15 @@ class GitClient { bool merge = false, bool keep = false, }) async { - final result = await raw( - [ - 'reset', - if (soft) '--soft', - if (mixed) '--mixed', - if (hard) '--hard', - if (merge) '--merge', - if (keep) '--keep', - if (ref != null) ref, - ], - directory: repository, - ); + final result = await raw([ + 'reset', + if (soft) '--soft', + if (mixed) '--mixed', + if (hard) '--hard', + if (merge) '--merge', + if (keep) '--keep', + if (ref != null) ref, + ], directory: repository); _ensureSuccess(result); } @@ -248,18 +227,15 @@ class GitClient { bool merge = false, bool keep = false, }) async { - final result = await raw( - [ - 'reset', - if (soft) '--soft', - if (mixed) '--mixed', - if (hard) '--hard', - if (merge) '--merge', - if (keep) '--keep', - if (ref != null) ref, - ], - directory: repository, - ); + final result = await raw([ + 'reset', + if (soft) '--soft', + if (mixed) '--mixed', + if (hard) '--hard', + if (merge) '--merge', + if (keep) '--keep', + if (ref != null) ref, + ], directory: repository); return result.exitCode == 0; } @@ -269,14 +245,11 @@ class GitClient { String? remote, bool all = false, }) async { - final result = await raw( - [ - 'pull', - if (remote != null) remote, - if (all) '--all', - ], - directory: repository, - ); + final result = await raw([ + 'pull', + if (remote != null) remote, + if (all) '--all', + ], directory: repository); _ensureSuccess(result); } @@ -288,16 +261,13 @@ class GitClient { bool all = false, bool updateHeadOk = false, }) async { - final result = await raw( - [ - 'fetch', - if (all) '--all', - if (updateHeadOk) '--update-head-ok', - if (!all) remote, - if (ref != null) ref, - ], - directory: repository, - ); + final result = await raw([ + 'fetch', + if (all) '--all', + if (updateHeadOk) '--update-head-ok', + if (!all) remote, + if (ref != null) ref, + ], directory: repository); _ensureSuccess(result); } @@ -308,16 +278,13 @@ class GitClient { bool? fastForward, bool fastForwardOnly = false, }) async { - final result = await raw( - [ - 'merge', - if (fastForward != null) - if (fastForward) '--ff' else '--no-ff', - if (fastForwardOnly) '--ff-only', - fromCommit, - ], - directory: repository, - ); + final result = await raw([ + 'merge', + if (fastForward != null) + if (fastForward) '--ff' else '--no-ff', + if (fastForwardOnly) '--ff-only', + fromCommit, + ], directory: repository); _ensureSuccess(result); } @@ -328,15 +295,12 @@ class GitClient { bool short = false, bool abbreviation = false, }) async { - final result = await raw( - [ - 'rev-parse', - if (short) '--short', - if (abbreviation) '--abbrev-ref', - ...args, - ], - directory: repository, - ); + final result = await raw([ + 'rev-parse', + if (short) '--short', + if (abbreviation) '--abbrev-ref', + ...args, + ], directory: repository); _ensureSuccess(result); return (result.stdout as String).trim().split('\n').toList(); } @@ -365,16 +329,13 @@ class GitClient { bool abbreviation = false, bool verify = false, }) async { - final result = await raw( - [ - 'rev-parse', - if (short) '--short', - if (abbreviation) '--abbrev-ref', - if (verify) '--verify', - ...args, - ], - directory: repository, - ); + final result = await raw([ + 'rev-parse', + if (short) '--short', + if (abbreviation) '--abbrev-ref', + if (verify) '--verify', + ...args, + ], directory: repository); if (result.exitCode != 0) { return null; } @@ -407,22 +368,13 @@ class GitClient { required String ref1, required String ref2, }) async { - final result = await raw( - [ - 'merge-base', - ref1, - ref2, - ], - directory: repository, - ); + final result = await raw(['merge-base', ref1, ref2], directory: repository); _ensureSuccess(result); return (result.stdout as String).trim(); } /// Returns true if the repository has uncomitted changes. - Future hasUncomittedChanges({ - required Directory repository, - }) async { + Future hasUncomittedChanges({required Directory repository}) async { await raw(['git update-index', '--refresh']); final result = await raw(['diff-index', '--quiet', 'HEAD', '--']); return result.exitCode != 0; @@ -447,11 +399,7 @@ class GitClient { bool short = false, String branch = 'HEAD', }) { - return tryRevParseSingle( - repository: repository, - short: short, - arg: branch, - ); + return tryRevParseSingle(repository: repository, short: short, arg: branch); } /// https://git-scm.com/docs/git-branch @@ -462,16 +410,13 @@ class GitClient { bool force = false, bool delete = false, }) async { - final result = await raw( - [ - 'branch', - if (force) '-f', - if (delete) '-d', - if (setUpstream != null) ...['-u', setUpstream], - branch, - ], - directory: repository, - ); + final result = await raw([ + 'branch', + if (force) '-f', + if (delete) '-d', + if (setUpstream != null) ...['-u', setUpstream], + branch, + ], directory: repository); _ensureSuccess(result); } @@ -493,13 +438,10 @@ class GitClient { required Directory repository, required String branch, }) async { - final result = await raw( - [ - 'show-ref', - 'refs/heads/$branch', - ], - directory: repository, - ); + final result = await raw([ + 'show-ref', + 'refs/heads/$branch', + ], directory: repository); return result.exitCode == 0; } @@ -508,14 +450,7 @@ class GitClient { required Directory repository, required String branch, }) async { - final result = await raw( - [ - 'branch', - '-D', - branch, - ], - directory: repository, - ); + final result = await raw(['branch', '-D', branch], directory: repository); _ensureSuccess(result); } @@ -544,14 +479,11 @@ class GitClient { required String name, required String? ref, }) async { - final result = await raw( - [ - 'symbolic-ref', - name, - if (ref == null) '--delete' else ref, - ], - directory: repository, - ); + final result = await raw([ + 'symbolic-ref', + name, + if (ref == null) '--delete' else ref, + ], directory: repository); _ensureSuccess(result); } @@ -561,10 +493,7 @@ class GitClient { required List objects, }) async { final result = await raw( - [ - 'show', - ...objects, - ], + ['show', ...objects], directory: repository, binary: true, ); @@ -578,10 +507,7 @@ class GitClient { required List objects, }) async { final result = await raw( - [ - 'show', - ...objects, - ], + ['show', ...objects], directory: repository, binary: true, ); @@ -595,10 +521,7 @@ class GitClient { String ref = 'HEAD', required String path, }) { - return show( - repository: repository, - objects: ['$ref:$path'], - ); + return show(repository: repository, objects: ['$ref:$path']); } /// Checks if a file exists in the repository. @@ -621,10 +544,7 @@ class GitClient { String ref = 'HEAD', required String path, }) { - return tryShow( - repository: repository, - objects: ['$ref:$path'], - ); + return tryShow(repository: repository, objects: ['$ref:$path']); } /// Gets all of the names of tags containing the provided ref. @@ -634,10 +554,11 @@ class GitClient { required Directory repository, required String ref, }) async { - final result = await raw( - ['tag', '--points-at', ref], - directory: repository, - ); + final result = await raw([ + 'tag', + '--points-at', + ref, + ], directory: repository); return (result.stdout as String).trim().split('\n'); } @@ -649,16 +570,13 @@ class GitClient { String? match, bool long = false, }) async { - final result = await raw( - [ - 'describe', - if (tags) '--tags', - if (long) '--long', - if (match != null) ...['--match', match], - ref, - ], - directory: repository, - ); + final result = await raw([ + 'describe', + if (tags) '--tags', + if (long) '--long', + if (match != null) ...['--match', match], + ref, + ], directory: repository); return (result.stdout as String).trim(); } @@ -670,10 +588,7 @@ class GitClient { Future> getRemotes({ required Directory repository, }) async { - final result = await raw( - ['remote', '-v'], - directory: repository, - ); + final result = await raw(['remote', '-v'], directory: repository); _ensureSuccess(result); final stdout = result.stdout as String; final fetches = {}; @@ -710,16 +625,13 @@ class GitClient { required String url, bool fetch = false, }) async { - final result = await raw( - [ - 'remote', - 'add', - if (fetch) '-f', - name, - url, - ], - directory: repository, - ); + final result = await raw([ + 'remote', + 'add', + if (fetch) '-f', + name, + url, + ], directory: repository); _ensureSuccess(result); } @@ -732,18 +644,15 @@ class GitClient { bool add = false, bool delete = false, }) async { - final result = await raw( - [ - 'remote', - 'set-url', - if (push) '--push', - if (delete) '--delete', - if (add) '--add', - name, - url, - ], - directory: repository, - ); + final result = await raw([ + 'remote', + 'set-url', + if (push) '--push', + if (delete) '--delete', + if (add) '--add', + name, + url, + ], directory: repository); _ensureSuccess(result); } @@ -752,14 +661,7 @@ class GitClient { required Directory repository, required String name, }) async { - final result = await raw( - [ - 'remote', - 'remove', - name, - ], - directory: repository, - ); + final result = await raw(['remote', 'remove', name], directory: repository); _ensureSuccess(result); } @@ -779,10 +681,7 @@ class GitClient { } if (currentRemotes.containsKey(remoteName)) { // Delete existing remote - await removeRemote( - repository: repository, - name: remoteName, - ); + await removeRemote(repository: repository, name: remoteName); } final newRemote = entry.value; if (newRemote != null) { @@ -832,15 +731,12 @@ class GitClient { required Iterable files, bool value = true, }) async { - final result = await raw( - [ - 'update-index', - if (value) '--assume-unchanged' else '--no-assume-unchanged', - '--', - ...files, - ], - directory: repository, - ); + final result = await raw([ + 'update-index', + if (value) '--assume-unchanged' else '--no-assume-unchanged', + '--', + ...files, + ], directory: repository); _ensureSuccess(result); } @@ -849,14 +745,7 @@ class GitClient { required Directory repository, required String name, }) async { - final result = await raw( - [ - 'config', - '--get', - name, - ], - directory: repository, - ); + final result = await raw(['config', '--get', name], directory: repository); if (result.exitCode == 1) return null; _ensureSuccess(result); return (result.stdout as String).trim(); @@ -868,14 +757,7 @@ class GitClient { required String name, required String value, }) async { - final result = await raw( - [ - 'config', - name, - value, - ], - directory: repository, - ); + final result = await raw(['config', name, value], directory: repository); _ensureSuccess(result); } @@ -887,14 +769,9 @@ class GitClient { @immutable class GitRemoteUrls { - const GitRemoteUrls({ - required this.fetch, - required this.push, - }); + const GitRemoteUrls({required this.fetch, required this.push}); - GitRemoteUrls.single(String url) - : fetch = url, - push = {url}; + GitRemoteUrls.single(String url) : fetch = url, push = {url}; final String fetch; final Set push; @@ -1007,31 +884,37 @@ class GitTagVersion { /// return '1.2.3-4.5.pre-6-gabc123'). static GitTagVersion parse(String version) { final RegExp versionPattern = RegExp( - r'^(\d+)\.(\d+)\.(\d+)(-\d+\.\d+\.pre)?(?:-(\d+)-g([a-f0-9]+))?$'); + r'^(\d+)\.(\d+)\.(\d+)(-\d+\.\d+\.pre)?(?:-(\d+)-g([a-f0-9]+))?$', + ); final Match? match = versionPattern.firstMatch(version.trim()); if (match == null) { return unknown; } final List matchGroups = match.groups([1, 2, 3, 4, 5, 6]); - final int? x = - matchGroups[0] == null ? null : int.tryParse(matchGroups[0]!); - final int? y = - matchGroups[1] == null ? null : int.tryParse(matchGroups[1]!); - final int? z = - matchGroups[2] == null ? null : int.tryParse(matchGroups[2]!); + final int? x = matchGroups[0] == null + ? null + : int.tryParse(matchGroups[0]!); + final int? y = matchGroups[1] == null + ? null + : int.tryParse(matchGroups[1]!); + final int? z = matchGroups[2] == null + ? null + : int.tryParse(matchGroups[2]!); final String? devString = matchGroups[3]; int? devVersion, devPatch; if (devString != null) { - final Match? devMatch = - RegExp(r'^-(\d+)\.(\d+)\.pre$').firstMatch(devString); + final Match? devMatch = RegExp( + r'^-(\d+)\.(\d+)\.pre$', + ).firstMatch(devString); final List? devGroups = devMatch?.groups([1, 2]); devVersion = devGroups?[0] == null ? null : int.tryParse(devGroups![0]!); devPatch = devGroups?[1] == null ? null : int.tryParse(devGroups![1]!); } // count of commits past last tagged version - final int? commits = - matchGroups[4] == null ? 0 : int.tryParse(matchGroups[4]!); + final int? commits = matchGroups[4] == null + ? 0 + : int.tryParse(matchGroups[4]!); final String? hash = matchGroups[5]; return GitTagVersion( diff --git a/puro/lib/src/http.dart b/puro/lib/src/http.dart index 77db576..583bddd 100644 --- a/puro/lib/src/http.dart +++ b/puro/lib/src/http.dart @@ -76,7 +76,8 @@ extension StreamedResponseExtensions on StreamedResponse { if (extraHeaders != null) ...extraHeaders, }, isRedirect: isRedirect ?? (keepIsRedirect && this.isRedirect), - persistentConnection: persistentConnection ?? + persistentConnection: + persistentConnection ?? (!keepPersistentConnection || this.persistentConnection), reasonPhrase: reasonPhrase ?? (keepReasonPhrase ? this.reasonPhrase : null), @@ -94,9 +95,7 @@ extension StreamedResponseExtensions on StreamedResponse { /// After the last call to [copyRequest] you should also call [close], this /// prevents the entire request body from buffering in memory. abstract class RequestCopier { - factory RequestCopier({ - required BaseRequest original, - }) = _RequestCopierImpl; + factory RequestCopier({required BaseRequest original}) = _RequestCopierImpl; const RequestCopier._(); @@ -126,8 +125,8 @@ const _sentinel = _Sentinel(); class _RequestCopierImpl extends RequestCopier { _RequestCopierImpl({required this.original}) - : splitter = StreamSplitter(original.finalize()), - super._(); + : splitter = StreamSplitter(original.finalize()), + super._(); final BaseRequest original; final StreamSplitter> splitter; @@ -172,11 +171,7 @@ class _RequestCopierImpl extends RequestCopier { } class HttpException implements Exception { - const HttpException({ - this.uri, - required this.statusCode, - required this.body, - }); + const HttpException({this.uri, required this.statusCode, required this.body}); factory HttpException.fromResponse(BaseResponse response) { // package:http responses are either a Response (body is known) or @@ -258,10 +253,7 @@ extension UriExtensions on Uri { userInfo: userInfo, host: host, port: port, - pathSegments: [ - ...pathSegments, - ...path.split('/'), - ], + pathSegments: [...pathSegments, ...path.split('/')], queryParameters: strQueryParameters.isEmpty ? null : strQueryParameters, fragment: fragment, ); diff --git a/puro/lib/src/install/bin.dart b/puro/lib/src/install/bin.dart index 47030a6..aa7519c 100644 --- a/puro/lib/src/install/bin.dart +++ b/puro/lib/src/install/bin.dart @@ -41,10 +41,7 @@ Future ensurePuroInstalled({ if (promote) { await _promoteStandalone(scope: scope); } else { - await _installTrampoline( - scope: scope, - force: force, - ); + await _installTrampoline(scope: scope, force: force); } await _installShims(scope: scope); await updateDefaultEnvSymlink(scope: scope); @@ -114,8 +111,9 @@ Future _installTrampoline({ installLocation = Platform.executable; break; case PuroInstallationType.development: - final puroDartFile = - version.packageRoot!.childDirectory('bin').childFile('puro.dart'); + final puroDartFile = version.packageRoot! + .childDirectory('bin') + .childFile('puro.dart'); command = '"${Platform.executable}" "${puroDartFile.path}"'; installLocation = puroDartFile.path; break; @@ -136,8 +134,9 @@ Future _installTrampoline({ : '$trampolineHeader\n$command "\$@"'; final trampolineExists = trampolineFile.existsSync(); - final executableExists = - executableIsTrampoline ? trampolineExists : executableFile.existsSync(); + final executableExists = executableIsTrampoline + ? trampolineExists + : executableFile.existsSync(); final installed = trampolineExists || executableExists; if (installed) { @@ -146,7 +145,8 @@ Future _installTrampoline({ // --x--x--x -> 0b001001001 -> 0x49 final needsChmod = !Platform.isWindows && trampolineStat.mode & 0x49 != 0x49; - final upToDate = exists && + final upToDate = + exists && await compareFileAtomic( scope: scope, file: trampolineFile, @@ -181,16 +181,15 @@ Future _installTrampoline({ } } -Future _installShims({ - required Scope scope, -}) async { +Future _installShims({required Scope scope}) async { final config = PuroConfig.of(scope); if (config.enableShims) { if (Platform.isWindows) { await writePassiveAtomic( scope: scope, file: config.puroDartShimFile, - content: '@echo off\n' + content: + '@echo off\n' 'SETLOCAL ENABLEDELAYEDEXPANSION\n' 'FOR %%i IN ("%~dp0.") DO SET PURO_BIN=%%~fi\n' '"%PURO_BIN%\\puro.exe" dart %* & exit /B !ERRORLEVEL!', @@ -198,7 +197,8 @@ Future _installShims({ await writePassiveAtomic( scope: scope, file: config.puroFlutterShimFile, - content: '@echo off\n' + content: + '@echo off\n' 'SETLOCAL ENABLEDELAYEDEXPANSION\n' 'FOR %%i IN ("%~dp0.") DO SET PURO_BIN=%%~fi\n' '"%PURO_BIN%\\puro.exe" flutter %* & exit /B !ERRORLEVEL!', @@ -207,26 +207,24 @@ Future _installShims({ await writePassiveAtomic( scope: scope, file: config.puroDartShimFile, - content: '$bashShimHeader\n' + content: + '$bashShimHeader\n' 'PURO_BIN="\$(cd "\${PROG_NAME%/*}" ; pwd -P)"\n' '"\$PURO_BIN/puro" dart "\$@"', ); await writePassiveAtomic( scope: scope, file: config.puroFlutterShimFile, - content: '$bashShimHeader\n' + content: + '$bashShimHeader\n' 'PURO_BIN="\$(cd "\${PROG_NAME%/*}" ; pwd -P)"\n' '"\$PURO_BIN/puro" flutter "\$@"', ); - await runProcess( - scope, - 'chmod', - [ - '+x', - config.puroDartShimFile.path, - config.puroFlutterShimFile.path, - ], - ); + await runProcess(scope, 'chmod', [ + '+x', + config.puroDartShimFile.path, + config.puroFlutterShimFile.path, + ]); } } else { if (config.puroDartShimFile.existsSync()) { diff --git a/puro/lib/src/install/profile.dart b/puro/lib/src/install/profile.dart index 4cb754f..7799f17 100644 --- a/puro/lib/src/install/profile.dart +++ b/puro/lib/src/install/profile.dart @@ -54,8 +54,11 @@ Future detectExternalFlutterInstallations({ offending.remove(config.puroFlutterShimFile.path); offending.remove(config.puroExecutableFile.path); - final defaultEnvBinDir = - config.getEnv('default', resolve: false).flutter.binDir.path; + final defaultEnvBinDir = config + .getEnv('default', resolve: false) + .flutter + .binDir + .path; offending.removeWhere((e) => path.equals(path.dirname(e), defaultEnvBinDir)); log.d('defaultEnvBinDir: $defaultEnvBinDir'); @@ -65,13 +68,10 @@ Future detectExternalFlutterInstallations({ if (offending.isNotEmpty) { return CommandMessage.format( - (format) => 'Other Flutter or Dart installations detected\n' + (format) => + 'Other Flutter or Dart installations detected\n' 'Puro recommends removing the following from your PATH:\n' - '${offending.map((e) => '${format.color( - '*', - bold: true, - foregroundColor: Ansi8BitColor.red, - )} $e').join('\n')}', + '${offending.map((e) => '${format.color('*', bold: true, foregroundColor: Ansi8BitColor.red)} $e').join('\n')}', type: CompletionType.alert, ); } else { @@ -97,32 +97,27 @@ Future updateProfile({ required Iterable lines, }) { final export = lines.map((e) => '$e $_kProfileComment').join('\n'); - return lockFile( - scope, - file, - (handle) async { - final contents = await handle.readAllAsString(); - if (export.isNotEmpty && contents.contains(export)) { - // Already exported - return false; - } - final lines = contents.split('\n'); - final originalLines = lines.length; - lines.removeWhere((e) => e.endsWith(_kProfileComment)); - if (export.isEmpty && lines.length == originalLines) { - // Not exporting anything - return false; - } - while (lines.isNotEmpty && lines.last.isEmpty) { - lines.removeLast(); - } - lines.add(''); - lines.add(export); - await handle.writeAllString('${lines.join('\n')}\n'); - return true; - }, - mode: FileMode.append, - ); + return lockFile(scope, file, (handle) async { + final contents = await handle.readAllAsString(); + if (export.isNotEmpty && contents.contains(export)) { + // Already exported + return false; + } + final lines = contents.split('\n'); + final originalLines = lines.length; + lines.removeWhere((e) => e.endsWith(_kProfileComment)); + if (export.isEmpty && lines.length == originalLines) { + // Not exporting anything + return false; + } + while (lines.isNotEmpty && lines.last.isEmpty) { + lines.removeLast(); + } + lines.add(''); + lines.add(export); + await handle.writeAllString('${lines.join('\n')}\n'); + return true; + }, mode: FileMode.append); } Future installProfileEnv({ @@ -148,7 +143,7 @@ Future installProfileEnv({ 'export PATH="\$PATH:${path.replaceAll(home, '\$HOME')}"', 'export PURO_ROOT="${config.puroRoot.path}"', if (config.legacyPubCache) - 'export PUB_CACHE="${config.legacyPubCacheDir.path}"' + 'export PUB_CACHE="${config.legacyPubCacheDir.path}"', ], ); return result ? file : null; @@ -167,11 +162,7 @@ Future uninstallProfileEnv({ if (file == null) { return null; } - final result = await updateProfile( - scope: scope, - file: file, - lines: [], - ); + final result = await updateProfile(scope: scope, file: file, lines: []); return result ? file : null; } @@ -248,16 +239,19 @@ Future readWindowsRegistryValue({ required String valueName, }) async { // This is horrible. - final result = await runProcess( - scope, - 'reg', - ['query', key, '/v', valueName], - ); + final result = await runProcess(scope, 'reg', [ + 'query', + key, + '/v', + valueName, + ]); if (result.exitCode != 0) { return null; } - final lines = - (result.stdout as String).replaceAll('\r\n', '\n').trim().split('\n'); + final lines = (result.stdout as String) + .replaceAll('\r\n', '\n') + .trim() + .split('\n'); if (lines.length != 2) { return null; } @@ -291,16 +285,10 @@ Future writeWindowsRegistryValue({ final log = PuroLogger.of(scope); final ProcessResult result; if (elevated) { - final startProc = 'Start-Process reg -Wait -Verb runAs -ArgumentList ' + final startProc = + 'Start-Process reg -Wait -Verb runAs -ArgumentList ' '${args.map(escapePowershellString).map((e) => '"$e"').join(',')}'; - result = await runProcess( - scope, - 'powershell', - [ - '-command', - startProc, - ], - ); + result = await runProcess(scope, 'powershell', ['-command', startProc]); } else { result = await runProcess(scope, 'reg', args); } @@ -316,33 +304,22 @@ Future deleteWindowsRegistryValue({ required String valueName, bool elevated = false, }) async { - final args = [ - 'delete', - key, - '/v', - valueName, - '/f', - ]; + final args = ['delete', key, '/v', valueName, '/f']; final log = PuroLogger.of(scope); final ProcessResult result; if (elevated) { - final startProc = 'Start-Process reg -Wait -Verb runAs -ArgumentList ' + final startProc = + 'Start-Process reg -Wait -Verb runAs -ArgumentList ' '${args.map(escapePowershellString).map((e) => '"$e"').join(',')}'; - result = await runProcess( - scope, - 'powershell', - [ - '-command', - startProc, - ], - ); + result = await runProcess(scope, 'powershell', ['-command', startProc]); } else { result = await runProcess(scope, 'reg', args); } if (result.exitCode != 0) { log.w( - 'reg delete failed with exit code ${result.exitCode}\n${result.stderr}'); + 'reg delete failed with exit code ${result.exitCode}\n${result.stderr}', + ); } return result.exitCode == 0; } @@ -394,9 +371,7 @@ Future tryDeleteWindowsEnv({ ); } -Future tryUpdateWindowsPath({ - required Scope scope, -}) async { +Future tryUpdateWindowsPath({required Scope scope}) async { final config = PuroConfig.of(scope); final env = { @@ -419,10 +394,7 @@ Future tryUpdateWindowsPath({ var result = false; - if (await tryUpdateWindowsEnv( - scope: scope, - env: env, - )) { + if (await tryUpdateWindowsEnv(scope: scope, env: env)) { result = true; } @@ -438,9 +410,7 @@ Future tryUpdateWindowsPath({ return result; } -Future tryCleanWindowsPath({ - required Scope scope, -}) async { +Future tryCleanWindowsPath({required Scope scope}) async { final config = PuroConfig.of(scope); final currentPath = await readWindowsRegistryValue( scope: scope, diff --git a/puro/lib/src/install/upgrade.dart b/puro/lib/src/install/upgrade.dart index 79c30c2..096f35c 100644 --- a/puro/lib/src/install/upgrade.dart +++ b/puro/lib/src/install/upgrade.dart @@ -24,7 +24,8 @@ Future upgradePuro({ await downloadFile( scope: scope, url: config.puroBuildsUrl.append( - path: '$targetVersion/' + path: + '$targetVersion/' '${buildTarget.name}/' '${buildTarget.executableName}', ), @@ -38,20 +39,18 @@ Future upgradePuro({ tempFile.renameSync(config.puroExecutableFile.path); terminal.flushStatus(); - final installProcess = await startProcess( - scope, - config.puroExecutableFile.path, - [ - if (terminal.enableColor) '--color', - if (terminal.enableStatus) '--progress', - '--log-level=${log.level?.index ?? 0}', - 'install-puro', - if (path != null) - if (path) '--path' else '--no-path', - ], - ); - final stdoutFuture = - installProcess.stdout.listen(stdout.add).asFuture(); + final installProcess = + await startProcess(scope, config.puroExecutableFile.path, [ + if (terminal.enableColor) '--color', + if (terminal.enableStatus) '--progress', + '--log-level=${log.level?.index ?? 0}', + 'install-puro', + if (path != null) + if (path) '--path' else '--no-path', + ]); + final stdoutFuture = installProcess.stdout + .listen(stdout.add) + .asFuture(); await installProcess.stderr.listen(stderr.add).asFuture(); await stdoutFuture; return installProcess.exitCode; diff --git a/puro/lib/src/json_edit/editor.dart b/puro/lib/src/json_edit/editor.dart index f5c1e40..8a9e860 100644 --- a/puro/lib/src/json_edit/editor.dart +++ b/puro/lib/src/json_edit/editor.dart @@ -6,10 +6,7 @@ import 'element.dart'; import 'grammar.dart'; class JsonEditor { - JsonEditor({ - required this.source, - required this.indentLevel, - }); + JsonEditor({required this.source, required this.indentLevel}); String source; final int indentLevel; @@ -18,11 +15,9 @@ class JsonEditor { String _encodeWithoutIndent(Object? value) { // Hack to keep space between elements - return const JsonEncoder.withIndent('') - .convert(value) - .split('\n') - .join() - .trim(); + return const JsonEncoder.withIndent( + '', + ).convert(value).split('\n').join().trim(); } Object? _wrapWithSelector(Iterable selectors, Object? value) { @@ -81,11 +76,7 @@ class JsonEditor { } } - void update( - List selectors, - Object? value, { - bool create = false, - }) { + void update(List selectors, Object? value, {bool create = false}) { if (selectors.isEmpty) { source = _indentedEncoder.convert(value); return; @@ -154,8 +145,10 @@ class JsonEditor { final collectionElement = collectionToken.value; final collectionIndent = _indentAt(collectionToken.start); final collectionStartLine = collectionToken.line; - final collectionEndLine = - Token.lineAndColumnOf(collectionToken.buffer, collectionToken.stop)[0]; + final collectionEndLine = Token.lineAndColumnOf( + collectionToken.buffer, + collectionToken.stop, + )[0]; final collectionSpace = collectionElement is JsonMap ? collectionElement.space : (collectionElement as JsonArray).space; @@ -199,7 +192,8 @@ class JsonEditor { if (lastChild is JsonWhitespace) { lastChildTrailingSpace = lastChild.trailing; } - replaceStart = collectionElement.children.last.stop - + replaceStart = + collectionElement.children.last.stop - lastChildTrailingSpace.length; leading = ',${lastChildTrailingSpace.trimRight()}'; } else { @@ -253,17 +247,15 @@ class JsonEditor { encodedValue = _encodeWithoutIndent(value); } - source = source.substring(0, replaceStart) + + source = + source.substring(0, replaceStart) + leading + encodedValue + trailing + source.substring(replaceEnd); } - void remove( - List selectors, { - bool permissive = true, - }) { + void remove(List selectors, {bool permissive = true}) { if (selectors.isEmpty) { throw ArgumentError('Cannot delete root'); } @@ -360,14 +352,16 @@ class JsonEditor { final hasChildBefore = elementIndex > 0; final hasChildAfter = elementIndex + 1 < collectionChildren.length; final collectionStartLine = collectionToken.line; - final collectionEndLine = - Token.lineAndColumnOf(collectionToken.buffer, collectionToken.stop)[0]; + final collectionEndLine = Token.lineAndColumnOf( + collectionToken.buffer, + collectionToken.stop, + )[0]; final singleLine = collectionStartLine == collectionEndLine; final leadingLines = leadingSpaceOf(token.value).split('\n'); final leadingLineAfterComma = !singleLine && hasChildBefore && leadingLines.isNotEmpty - ? leadingLines.first - : ''; + ? leadingLines.first + : ''; if (collectionElement.children.length == 1 && trailingSpaceAfterEOL.trim().isEmpty) { @@ -399,15 +393,13 @@ class JsonEditor { } } - source = source.substring(0, replaceStart) + + source = + source.substring(0, replaceStart) + content + source.substring(replaceEnd); } - Token? query( - List selectors, { - bool permissive = true, - }) { + Token? query(List selectors, {bool permissive = true}) { Token? token = JsonGrammar.parse(source); var selectorDesc = 'data'; for (var i = 0; i < selectors.length; i++) { diff --git a/puro/lib/src/json_edit/element.dart b/puro/lib/src/json_edit/element.dart index 70b9506..b7ba42c 100644 --- a/puro/lib/src/json_edit/element.dart +++ b/puro/lib/src/json_edit/element.dart @@ -58,10 +58,7 @@ class JsonWhitespace extends JsonElement { } class JsonMap extends JsonElement { - JsonMap({ - required this.children, - required this.space, - }); + JsonMap({required this.children, required this.space}); @override final List> children; @@ -69,9 +66,9 @@ class JsonMap extends JsonElement { Token? operator [](String key) { return children.cast?>().firstWhere( - (child) => child!.value.key.value == key, - orElse: () => null, - ); + (child) => child!.value.key.value == key, + orElse: () => null, + ); } @override @@ -108,10 +105,7 @@ class JsonMap extends JsonElement { } class JsonArray extends JsonElement { - JsonArray({ - required this.children, - required this.space, - }); + JsonArray({required this.children, required this.space}); @override List> children; @@ -123,9 +117,7 @@ class JsonArray extends JsonElement { @override Object? toJson() { - return [ - for (final child in children) child.value.toJson(), - ]; + return [for (final child in children) child.value.toJson()]; } @override diff --git a/puro/lib/src/json_edit/grammar.dart b/puro/lib/src/json_edit/grammar.dart index aa53cd5..9c662e1 100644 --- a/puro/lib/src/json_edit/grammar.dart +++ b/puro/lib/src/json_edit/grammar.dart @@ -10,10 +10,10 @@ class JsonGrammar extends GrammarDefinition> { Parser> start() => ref0(element).end(); Parser> element() => [ - literalElement(), - mapElement(), - arrayElement(), - ].toChoiceParser(failureJoiner: selectFarthestJoined).cast(); + literalElement(), + mapElement(), + arrayElement(), + ].toChoiceParser(failureJoiner: selectFarthestJoined).cast(); Parser lineComment() { return (string('//') & Token.newlineParser().neg().star()).flatten(); @@ -29,9 +29,9 @@ class JsonGrammar extends GrammarDefinition> { } Parser> token(Parser parser) { - return (ref0(space) & parser.token() & ref0(space)) - .token() - .map((token) { + return (ref0(space) & parser.token() & ref0(space)).token().map(( + token, + ) { final res = token.value; final leading = res[0] as String; final body = res[1] as Token; @@ -40,11 +40,7 @@ class JsonGrammar extends GrammarDefinition> { return body; } else { return Token( - JsonWhitespace( - leading: leading, - body: body, - trailing: trailing, - ), + JsonWhitespace(leading: leading, body: body, trailing: trailing), token.buffer, token.start, token.stop, @@ -79,66 +75,68 @@ class JsonGrammar extends GrammarDefinition> { Parser trueLiteral() => string('true').map((_) => true); Parser falseLiteral() => string('false').map((_) => false); Parser nullLiteral() => string('null').map((_) {}); - Parser numLiteral() => (char('-').optional() & - char('0').or(digit().plus()) & - char('.').seq(digit().plus()).optional() & - pattern('eE') - .seq(pattern('-+').optional()) - .seq(digit().plus()) - .optional()) - .flatten() - .map(num.parse); + Parser numLiteral() => + (char('-').optional() & + char('0').or(digit().plus()) & + char('.').seq(digit().plus()).optional() & + pattern( + 'eE', + ).seq(pattern('-+').optional()).seq(digit().plus()).optional()) + .flatten() + .map(num.parse); Parser> literalElement() { - return token([ - ref0(trueLiteral), - ref0(falseLiteral), - ref0(nullLiteral), - ref0(numLiteral), - ref0(stringLiteral), - ].toChoiceParser().token().map( - (str) => JsonLiteral(value: str), - )); + return token( + [ + ref0(trueLiteral), + ref0(falseLiteral), + ref0(nullLiteral), + ref0(numLiteral), + ref0(stringLiteral), + ].toChoiceParser().token().map((str) => JsonLiteral(value: str)), + ); } Parser> mapElement() { - return token((char('{') & - ref0(mapEntryElement) - .plusSeparated(char(',')) - .map((e) => e.elements) - .optional() & - ref0(space) & - char(',').optional() & - ref0(space) & - char('}')) - .map((res) { - return JsonMap( - children: (res[1] as List? ?? []) - .cast>() - .toList(), - space: res[2] as String, - ); - })); + return token( + (char('{') & + ref0( + mapEntryElement, + ).plusSeparated(char(',')).map((e) => e.elements).optional() & + ref0(space) & + char(',').optional() & + ref0(space) & + char('}')) + .map((res) { + return JsonMap( + children: (res[1] as List? ?? []) + .cast>() + .toList(), + space: res[2] as String, + ); + }), + ); } Parser> arrayElement() { - return token((char('[') & - ref0(element) - .plusSeparated(char(',')) - .map((e) => e.elements) - .optional() & - ref0(space) & - char(',').optional() & - ref0(space) & - char(']')) - .map((res) { - return JsonArray( - children: (res[1] as List? ?? []) - .cast>() - .toList(), - space: res[2] as String, - ); - })); + return token( + (char('[') & + ref0( + element, + ).plusSeparated(char(',')).map((e) => e.elements).optional() & + ref0(space) & + char(',').optional() & + ref0(space) & + char(']')) + .map((res) { + return JsonArray( + children: (res[1] as List? ?? []) + .cast>() + .toList(), + space: res[2] as String, + ); + }), + ); } Parser> mapEntryElement() { @@ -148,13 +146,14 @@ class JsonGrammar extends GrammarDefinition> { char(':') & ref0(element)) .map((res) { - return JsonMapEntry( - beforeKey: res[0] as String, - key: res[1] as Token, - afterKey: res[2] as String, - value: res[4] as Token, - ); - }).token(); + return JsonMapEntry( + beforeKey: res[0] as String, + key: res[1] as Token, + afterKey: res[2] as String, + value: res[4] as Token, + ); + }) + .token(); } static const escapeChars = { @@ -165,6 +164,6 @@ class JsonGrammar extends GrammarDefinition> { 'f': '\f', 'n': '\n', 'r': '\r', - 't': '\t' + 't': '\t', }; } diff --git a/puro/lib/src/logger.dart b/puro/lib/src/logger.dart index 7666897..b257925 100644 --- a/puro/lib/src/logger.dart +++ b/puro/lib/src/logger.dart @@ -40,12 +40,7 @@ class LogEntry { } class PuroLogger { - PuroLogger({ - this.level, - this.terminal, - this.onAdd, - this.profile = false, - }); + PuroLogger({this.level, this.terminal, this.onAdd, this.profile = false}); LogLevel? level; Terminal? terminal; @@ -74,15 +69,17 @@ class PuroLogger { if (profile) { final elapsed = stopwatch.elapsedMilliseconds; - buf.write(format.color( - '${elapsed.pretty().padLeft(4)} ', - foregroundColor: elapsed > 1000 - ? Ansi8BitColor.red - : elapsed > 100 - ? Ansi8BitColor.orange1 - : Ansi8BitColor.green, - bold: true, - )); + buf.write( + format.color( + '${elapsed.pretty().padLeft(4)} ', + foregroundColor: elapsed > 1000 + ? Ansi8BitColor.red + : elapsed > 100 + ? Ansi8BitColor.orange1 + : Ansi8BitColor.green, + bold: true, + ), + ); if (!stopwatch.isRunning) { stopwatch.start(); } else { @@ -90,11 +87,13 @@ class PuroLogger { } } - buf.write(format.color( - levelPrefixes[event.level]!, - foregroundColor: levelColors[event.level]!, - bold: true, - )); + buf.write( + format.color( + levelPrefixes[event.level]!, + foregroundColor: levelColors[event.level]!, + bold: true, + ), + ); terminal!.writeln(format.prefix('$buf ', event.message)); } diff --git a/puro/lib/src/process.dart b/puro/lib/src/process.dart index a301b03..74f68cb 100644 --- a/puro/lib/src/process.dart +++ b/puro/lib/src/process.dart @@ -29,24 +29,17 @@ Future startProcess( if (engineInfo.os == EngineOS.macOS && engineInfo.arch == EngineArch.arm64) { log.d('querying arch of $executable'); - final fileResult = await runProcess( - scope, - 'file', - [executable], - throwOnFailure: true, - ); + final fileResult = await runProcess(scope, 'file', [ + executable, + ], throwOnFailure: true); log.d('file result: ${fileResult.stdout}'); - if ((fileResult.stdout as String) - .contains('Mach-O 64-bit executable arm64')) { + if ((fileResult.stdout as String).contains( + 'Mach-O 64-bit executable arm64', + )) { return startProcess( scope, '/usr/bin/arch', - [ - '-arch', - 'arm64', - executable, - ...arguments, - ], + ['-arch', 'arm64', executable, ...arguments], workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: includeParentEnvironment, @@ -67,16 +60,17 @@ Future startProcess( mode: mode, ); final executableName = path.basename(executable); - log.d('[$executableName ${process.pid}] ${workingDirectory ?? ''}> ${[ - executable, - ...arguments - ].join(' ')}'); - unawaited(process.exitCode.then((exitCode) { - final log = PuroLogger.of(scope); - log.d( - '[$executableName ${process.pid}] finished with exit code $exitCode in ${DateTime.now().difference(start).inMilliseconds}ms', - ); - })); + log.d( + '[$executableName ${process.pid}] ${workingDirectory ?? ''}> ${[executable, ...arguments].join(' ')}', + ); + unawaited( + process.exitCode.then((exitCode) { + final log = PuroLogger.of(scope); + log.d( + '[$executableName ${process.pid}] finished with exit code $exitCode in ${DateTime.now().difference(start).inMilliseconds}ms', + ); + }), + ); return process; } @@ -215,13 +209,10 @@ Future runProcessWithTimeout( var stderrDone = false; int? exitCode; - final timer = Timer( - timeout, - () { - process.kill(timeoutSignal); - completer.complete(null); - }, - ); + final timer = Timer(timeout, () { + process.kill(timeoutSignal); + completer.complete(null); + }); void onDone() { if (!stdoutDone || @@ -257,10 +248,12 @@ Future runProcessWithTimeout( }, ); - unawaited(process.exitCode.then((value) { - exitCode = value; - onDone(); - })); + unawaited( + process.exitCode.then((value) { + exitCode = value; + onDone(); + }), + ); return completer.future; } @@ -277,21 +270,14 @@ class PsInfo { String toString() => '$id: $name'; } -Future> getParentProcesses({ - required Scope scope, -}) async { +Future> getParentProcesses({required Scope scope}) async { final log = PuroLogger.of(scope); final stack = []; if (Platform.isWindows) { - final result = await runProcess( - scope, - 'powershell', - [ - '-command', - 'Get-WmiObject -Query "select Name,ParentProcessId,ProcessId from Win32_Process" | ConvertTo-Json', - ], - debugLogging: false, - ); + final result = await runProcess(scope, 'powershell', [ + '-command', + 'Get-WmiObject -Query "select Name,ParentProcessId,ProcessId from Win32_Process" | ConvertTo-Json', + ], debugLogging: false); if (result.exitCode != 0 || (result.stdout as String).isEmpty) { if (result.stdout != '') log.w(result.stdout as String); if (result.stderr != '') log.w(result.stderr as String); @@ -335,8 +321,9 @@ Future> getParentProcesses({ '$pid', ]); if (result.exitCode != 0) break; - final resultMatch = RegExp(r'^\s*(\d+)\s+(.+)$') - .firstMatch((result.stdout as String).trim().split('\n').last); + final resultMatch = RegExp( + r'^\s*(\d+)\s+(.+)$', + ).firstMatch((result.stdout as String).trim().split('\n').last); if (resultMatch == null) break; final ppid = int.tryParse(resultMatch.group(1) ?? ''); final name = resultMatch.group(2)?.split(' ').first.split('/').last; diff --git a/puro/lib/src/progress.dart b/puro/lib/src/progress.dart index e2c0f5d..f5b5852 100644 --- a/puro/lib/src/progress.dart +++ b/puro/lib/src/progress.dart @@ -46,9 +46,7 @@ abstract class ProgressNode { }) async { final start = clock.now(); final log = PuroLogger.of(scope); - final node = ActiveProgressNode( - scope: OverrideScope(parent: scope), - ); + final node = ActiveProgressNode(scope: OverrideScope(parent: scope)); node.scope.add(ProgressNode.provider, node); scheduleMicrotask(() { addNode(node); @@ -165,10 +163,7 @@ class ActiveProgressNode extends ProgressNode { return (_progress! / _progressTotal!).clamp(0.0, 1.0); } - static String _indentString( - String input, - String indent, - ) { + static String _indentString(String input, String indent) { return input.split('\n').map((e) => '$indent$e').join('\n'); } @@ -193,19 +188,15 @@ class ActiveProgressNode extends ProgressNode { text = '$text $description'; } if (children.isNotEmpty) { - text = '$text\n${_indentString( - '${children.where((e) => e.visible).map((e) => e.render()).join('\n')}', - ' ', - )}'; + text = + '$text\n${_indentString('${children.where((e) => e.visible).map((e) => e.render()).join('\n')}', ' ')}'; } return text; } } class RootProgressNode extends ProgressNode { - RootProgressNode({ - required super.scope, - }) { + RootProgressNode({required super.scope}) { _onChanged = () { terminal.status = render(); }; diff --git a/puro/lib/src/provider.dart b/puro/lib/src/provider.dart index 26266d5..edcdeba 100644 --- a/puro/lib/src/provider.dart +++ b/puro/lib/src/provider.dart @@ -2,9 +2,7 @@ abstract class Provider { factory Provider(T Function(Scope scope) create) = LazyProvider; factory Provider.late() { - return Provider( - (scope) => throw AssertionError('Provider not in scope'), - ); + return Provider((scope) => throw AssertionError('Provider not in scope')); } ProviderNode createNode(Scope scope); diff --git a/puro/lib/src/terminal.dart b/puro/lib/src/terminal.dart index 45ab41d..212d34b 100644 --- a/puro/lib/src/terminal.dart +++ b/puro/lib/src/terminal.dart @@ -79,11 +79,7 @@ class OutputFormatter { CompletionType type = CompletionType.success, }) { return prefix( - color( - type.prefix, - foregroundColor: type.color, - bold: true, - ), + color(type.prefix, foregroundColor: type.color, bold: true), content, ); } @@ -124,12 +120,11 @@ class ColorOutputFormatter extends OutputFormatter { } class Terminal implements StringSink { - Terminal({ - required this.stdout, - }); + Terminal({required this.stdout}); final Stdout stdout; - late var enableColor = stdout.supportsAnsiEscapes || + late var enableColor = + stdout.supportsAnsiEscapes || (Platform.isWindows && (Platform.environment['TERM']?.contains('xterm') ?? false)); late var enableStatus = enableColor; diff --git a/puro/lib/src/version.dart b/puro/lib/src/version.dart index 48c716d..4751061 100644 --- a/puro/lib/src/version.dart +++ b/puro/lib/src/version.dart @@ -117,10 +117,7 @@ class PuroVersion { scriptPath, path.join(path.current, target.executableName), ) || - path.equals( - scriptPath, - path.join(path.current, 'puro'), - ) || + path.equals(scriptPath, path.join(path.current, 'puro')) || config.fileSystem .file(executablePath) .parent @@ -130,8 +127,9 @@ class PuroVersion { scriptPath = executablePath; } else { try { - scriptPath = - config.fileSystem.file(scriptPath).resolveSymbolicLinksSync(); + scriptPath = config.fileSystem + .file(scriptPath) + .resolveSymbolicLinksSync(); } catch (exception, stackTrace) { log.w('Error while resolving Platform.script\n$exception\n$stackTrace'); } @@ -145,10 +143,12 @@ class PuroVersion { if (!scriptIsExecutable && packageRoot == null) { final pubInstallBinDir = scriptFile.parent; final pubInstallPackageDir = pubInstallBinDir.parent; - final pubInstallPubspecLockFile = - pubInstallPackageDir.childFile('pubspec.lock'); - final pubInstallPubspecYamlFile = - pubInstallPackageDir.childFile('pubspec.yaml'); + final pubInstallPubspecLockFile = pubInstallPackageDir.childFile( + 'pubspec.lock', + ); + final pubInstallPubspecYamlFile = pubInstallPackageDir.childFile( + 'pubspec.yaml', + ); log.d('pubInstallBinDir: ${pubInstallBinDir.path}'); log.d('pubInstallPackageDir: ${pubInstallPackageDir.path}'); if (pubInstallBinDir.basename == 'bin' && @@ -161,8 +161,9 @@ class PuroVersion { final segments = path.split(scriptPath); final dartToolIndex = segments.indexOf('.dart_tool'); if (dartToolIndex != -1) { - packageRoot = - _fs.directory(path.joinAll(segments.take(dartToolIndex))); + packageRoot = _fs.directory( + path.joinAll(segments.take(dartToolIndex)), + ); } } } @@ -189,10 +190,7 @@ class PuroVersion { } } else if (scriptIsExecutable) { puroExecutable = config.fileSystem.file(executablePath); - if (path.equals( - executablePath, - config.puroExecutableFile.path, - )) { + if (path.equals(executablePath, config.puroExecutableFile.path)) { installationType = PuroInstallationType.distribution; } else { installationType = PuroInstallationType.standalone; @@ -225,7 +223,8 @@ class PuroVersion { ); } catch (exception, stackTrace) { log.w( - 'Error while parsing ${pubspecLockFile.path}\n$exception\n$stackTrace'); + 'Error while parsing ${pubspecLockFile.path}\n$exception\n$stackTrace', + ); } } } @@ -249,11 +248,7 @@ enum PuroBuildTarget { linuxX64('linux-x64', '', ''), macosX64('darwin-x64', '', ''); - const PuroBuildTarget( - this.name, - this.exeSuffix, - this.scriptSuffix, - ); + const PuroBuildTarget(this.name, this.exeSuffix, this.scriptSuffix); factory PuroBuildTarget.fromString(String str) { switch (str) { @@ -295,9 +290,7 @@ enum PuroBuildTarget { const _kUpdateVersionCheckThreshold = Duration(days: 1); const _kUpdateNotificationThreshold = Duration(days: 1); -Future _fetchLatestVersionInBackground({ - required Scope scope, -}) async { +Future _fetchLatestVersionInBackground({required Scope scope}) async { final log = PuroLogger.of(scope); final config = PuroConfig.of(scope); final httpClient = scope.read(clientProvider); @@ -336,8 +329,9 @@ Future checkIfUpdateAvailable({ return null; } log.v('Checking if update is available'); - final lastVersionCheck = - prefs.hasLastUpdateCheck() ? DateTime.parse(prefs.lastUpdateCheck) : null; + final lastVersionCheck = prefs.hasLastUpdateCheck() + ? DateTime.parse(prefs.lastUpdateCheck) + : null; final lastNotification = prefs.hasLastUpdateNotification() ? DateTime.parse(prefs.lastUpdateNotification) : null; @@ -348,11 +342,13 @@ Future checkIfUpdateAvailable({ final isOutOfDate = latestVersion != null && latestVersion > puroVersion.semver; final now = clock.now(); - final willNotify = isOutOfDate && + final willNotify = + isOutOfDate && (alwaysNotify || lastNotification == null || now.difference(lastNotification) > _kUpdateNotificationThreshold); - final shouldVersionCheck = !isOutOfDate && + final shouldVersionCheck = + !isOutOfDate && (lastVersionCheck == null || now.difference(lastVersionCheck) > _kUpdateVersionCheckThreshold); log.d('lastNotification: $lastNotification'); diff --git a/puro/lib/src/workspace/clean.dart b/puro/lib/src/workspace/clean.dart index d43929f..40216f0 100644 --- a/puro/lib/src/workspace/clean.dart +++ b/puro/lib/src/workspace/clean.dart @@ -15,41 +15,27 @@ Future restoreIdeConfigs({ required Directory projectDir, required ProjectConfig projectConfig, }) async { - runOptional( - scope, - 'restoring intellij config', - () async { - final ideConfig = await IntelliJConfig.load( - scope: scope, - projectDir: projectDir, - projectConfig: projectConfig, - ); - if (ideConfig.exists) { - await restoreIdeConfig( - scope: scope, - ideConfig: ideConfig, - ); - } - }, - ); + runOptional(scope, 'restoring intellij config', () async { + final ideConfig = await IntelliJConfig.load( + scope: scope, + projectDir: projectDir, + projectConfig: projectConfig, + ); + if (ideConfig.exists) { + await restoreIdeConfig(scope: scope, ideConfig: ideConfig); + } + }); - runOptional( - scope, - 'restoring vscode config', - () async { - final ideConfig = await VSCodeConfig.load( - scope: scope, - projectDir: projectDir, - projectConfig: projectConfig, - ); - if (ideConfig.exists) { - await restoreIdeConfig( - scope: scope, - ideConfig: ideConfig, - ); - } - }, - ); + runOptional(scope, 'restoring vscode config', () async { + final ideConfig = await VSCodeConfig.load( + scope: scope, + projectDir: projectDir, + projectConfig: projectConfig, + ); + if (ideConfig.exists) { + await restoreIdeConfig(scope: scope, ideConfig: ideConfig); + } + }); } Future restoreIdeConfig({ @@ -85,11 +71,7 @@ Future cleanWorkspace({ final config = PuroConfig.of(scope); projectDir ??= config.project.ensureParentProjectDir(); await runOptional(scope, 'restoring gitignore', () { - return updateGitignore( - scope: scope, - projectDir: projectDir!, - ignores: {}, - ); + return updateGitignore(scope: scope, projectDir: projectDir!, ignores: {}); }); await runOptional(scope, 'restoring IDE configs', () { return restoreIdeConfigs( diff --git a/puro/lib/src/workspace/gitignore.dart b/puro/lib/src/workspace/gitignore.dart index ee9a52f..de7dcdc 100644 --- a/puro/lib/src/workspace/gitignore.dart +++ b/puro/lib/src/workspace/gitignore.dart @@ -32,11 +32,13 @@ Future updateConfigLines({ if (!existingLines.containsAll(lines) || existingLines.length != lines.length) { log.v('Updating config at ${file.path}'); - file.writeAsStringSync([ - ...result, - '', - for (final name in lines) ...[gitConfigComment, name], - ].join('\n')); + file.writeAsStringSync( + [ + ...result, + '', + for (final name in lines) ...[gitConfigComment, name], + ].join('\n'), + ); for (final line in lines) { if (!existingLines.contains(line)) { log.v('Added "$line"'); @@ -56,11 +58,14 @@ Directory? findGitDir(Directory projectDir) { if (gitTree == null) return null; final gitDir = gitTree.childDirectory('.git'); if (fileSystem.statSync(gitDir.path).type == FileSystemEntityType.file) { - final match = RegExp(r'gitdir: (.+)') - .firstMatch(fileSystem.file(gitDir.path).readAsStringSync().trim()); + final match = RegExp( + r'gitdir: (.+)', + ).firstMatch(fileSystem.file(gitDir.path).readAsStringSync().trim()); if (match != null) { - final gitTree2 = - findProjectDir(fileSystem.directory(match.group(1)!), '.git'); + final gitTree2 = findProjectDir( + fileSystem.directory(match.group(1)!), + '.git', + ); if (gitTree2 != null) { return gitTree2.childDirectory('.git').resolve(); } diff --git a/puro/lib/src/workspace/install.dart b/puro/lib/src/workspace/install.dart index b131ea7..d297f96 100644 --- a/puro/lib/src/workspace/install.dart +++ b/puro/lib/src/workspace/install.dart @@ -24,55 +24,47 @@ Future installIdeConfigs({ final log = PuroLogger.of(scope); log.d('vscode override: $vscode'); log.d('intellij override: $vscode'); - await runOptional( - scope, - 'installing IntelliJ config', - () async { - final ideConfig = await IntelliJConfig.load( + await runOptional(scope, 'installing IntelliJ config', () async { + final ideConfig = await IntelliJConfig.load( + scope: scope, + projectDir: projectDir, + projectConfig: projectConfig, + ); + log.d('intellij exists: ${ideConfig.exists}'); + if ((ideConfig.exists || intellij == true) && + (replaceOnly == null || + (ideConfig.dartSdkDir?.absolute.pathEquals( + replaceOnly.flutter.cache.dartSdkDir, + ) == + true))) { + await installIdeConfig( scope: scope, - projectDir: projectDir, - projectConfig: projectConfig, + ideConfig: ideConfig, + environment: environment, ); - log.d('intellij exists: ${ideConfig.exists}'); - if ((ideConfig.exists || intellij == true) && - (replaceOnly == null || - (ideConfig.dartSdkDir?.absolute - .pathEquals(replaceOnly.flutter.cache.dartSdkDir) == - true))) { - await installIdeConfig( - scope: scope, - ideConfig: ideConfig, - environment: environment, - ); - } - }, - skip: intellij == false, - ); + } + }, skip: intellij == false); - await runOptional( - scope, - 'installing VSCode config', - () async { - final ideConfig = await VSCodeConfig.load( + await runOptional(scope, 'installing VSCode config', () async { + final ideConfig = await VSCodeConfig.load( + scope: scope, + projectDir: projectDir, + projectConfig: projectConfig, + ); + log.d('vscode exists: ${ideConfig.exists}'); + if ((ideConfig.exists || vscode == true) && + (replaceOnly == null || + (ideConfig.dartSdkDir?.absolute.pathEquals( + replaceOnly.flutter.cache.dartSdkDir, + ) == + true))) { + await installIdeConfig( scope: scope, - projectDir: projectDir, - projectConfig: projectConfig, + ideConfig: ideConfig, + environment: environment, ); - log.d('vscode exists: ${ideConfig.exists}'); - if ((ideConfig.exists || vscode == true) && - (replaceOnly == null || - (ideConfig.dartSdkDir?.absolute - .pathEquals(replaceOnly.flutter.cache.dartSdkDir) == - true))) { - await installIdeConfig( - scope: scope, - ideConfig: ideConfig, - environment: environment, - ); - } - }, - skip: vscode == false, - ); + } + }, skip: vscode == false); } Future installIdeConfig({ diff --git a/puro/lib/src/workspace/intellij.dart b/puro/lib/src/workspace/intellij.dart index 49166f7..16668b0 100644 --- a/puro/lib/src/workspace/intellij.dart +++ b/puro/lib/src/workspace/intellij.dart @@ -25,8 +25,9 @@ class IntelliJConfig extends IdeConfig { late final dartSdkBakFile = librariesDir.childFile('Dart_SDK.xml.bak'); late final dartPackagesFile = librariesDir.childFile('Dart_Packages.xml'); late final modulesXmlFile = configDir.childFile('modules.xml'); - late final dartPackagesBakFile = - librariesDir.childFile('Dart_Packages.xml.bak'); + late final dartPackagesBakFile = librariesDir.childFile( + 'Dart_Packages.xml.bak', + ); @override String get name => 'IntelliJ'; @@ -80,8 +81,8 @@ class IntelliJConfig extends IdeConfig { // The IntelliJ Dart plugin parses this file and walks its AST to extract // the keys of this library map. This is dumb. 💀 // https://github.com/JetBrains/intellij-plugins/blob/0f07ca63355d5530b441ca566c98f17c560e77f8/Dart/src/com/jetbrains/lang/dart/ide/index/DartLibraryIndex.java#L132 - final librariesFileLines = - await dartSdk.internalLibrariesDartFile.readAsLines(); + final librariesFileLines = await dartSdk.internalLibrariesDartFile + .readAsLines(); final startLine = librariesFileLines.indexOf( 'const Map libraries = const {', ); @@ -112,43 +113,37 @@ class IntelliJConfig extends IdeConfig { 'Failed to extract libraries from ${dartSdk.internalLibrariesDartFile.path}', ); } - final homeDirStr = - path.canonicalize(config.homeDir.path).replaceAll('\\', '/'); + final homeDirStr = path + .canonicalize(config.homeDir.path) + .replaceAll('\\', '/'); final urls = [ for (final libName in libraries) '${Uri.file(path.canonicalize(dartSdk.libDir.path))}/$libName' .replaceAll('$homeDirStr', r'$USER_HOME$'), ]; urls.sort(); - final document = XmlDocument( - [ - XmlElement( - XmlName('component'), - [XmlAttribute(XmlName('name'), 'libraryTable')], - [ - XmlElement( - XmlName('library'), - [XmlAttribute(XmlName('name'), 'Dart SDK')], - [ - XmlElement( - XmlName('CLASSES'), - [], - [ - for (final url in urls) - XmlElement( - XmlName('root'), - [XmlAttribute(XmlName('url'), url)], - ), - ], - ), - XmlElement(XmlName('JAVADOC')), - XmlElement(XmlName('SOURCES')), - ], - ), - ], - ), - ], - ); + final document = XmlDocument([ + XmlElement( + XmlName('component'), + [XmlAttribute(XmlName('name'), 'libraryTable')], + [ + XmlElement( + XmlName('library'), + [XmlAttribute(XmlName('name'), 'Dart SDK')], + [ + XmlElement(XmlName('CLASSES'), [], [ + for (final url in urls) + XmlElement(XmlName('root'), [ + XmlAttribute(XmlName('url'), url), + ]), + ]), + XmlElement(XmlName('JAVADOC')), + XmlElement(XmlName('SOURCES')), + ], + ), + ], + ), + ]); log.v('Writing to `${dartSdkFile.path}`'); dartSdkFile.parent.createSync(recursive: true); dartSdkFile.writeAsStringSync(document.toXmlString(pretty: true)); @@ -174,9 +169,11 @@ class IntelliJConfig extends IdeConfig { return; } - var component = project.childElements.firstWhereOrNull((e) => - e.name.toString() == 'component' && - e.getAttribute('name') == 'ProjectModuleManager'); + var component = project.childElements.firstWhereOrNull( + (e) => + e.name.toString() == 'component' && + e.getAttribute('name') == 'ProjectModuleManager', + ); if (component == null) { log.d('enableDartSupport - no component'); return; @@ -194,7 +191,8 @@ class IntelliJConfig extends IdeConfig { var filepath = e.getAttribute('filepath'); if (filepath == null || !filepath.startsWith(projectDirStr) || - !filepath.endsWith('.iml')) return false; + !filepath.endsWith('.iml')) + return false; filepath = filepath.substring(projectDirStr.length); if (filepath.startsWith(dotIdeaStr)) { filepath = filepath.substring(dotIdeaStr.length); @@ -206,8 +204,9 @@ class IntelliJConfig extends IdeConfig { return; } - var rootModulePath = - rootModule.getAttribute('filepath')!.substring(projectDirStr.length); + var rootModulePath = rootModule + .getAttribute('filepath')! + .substring(projectDirStr.length); if (Platform.isWindows) { rootModulePath = rootModulePath.replaceAll('/', '\\'); } @@ -226,9 +225,11 @@ class IntelliJConfig extends IdeConfig { return; } - component = module.childElements.firstWhereOrNull((e) => - e.name.toString() == 'component' && - e.getAttribute('name') == 'NewModuleRootManager'); + component = module.childElements.firstWhereOrNull( + (e) => + e.name.toString() == 'component' && + e.getAttribute('name') == 'NewModuleRootManager', + ); if (component == null) { log.d('enableDartSupport - no component in iml'); return; @@ -275,7 +276,8 @@ class IntelliJConfig extends IdeConfig { log.v('intellij workspaceDir: $workspaceDir'); if (workspaceDir == null) { return IntelliJConfig( - workspaceDir: config.findVSCodeWorkspaceDir(projectDir) ?? + workspaceDir: + config.findVSCodeWorkspaceDir(projectDir) ?? projectConfig.ensureParentProjectDir(), projectConfig: projectConfig, exists: false, @@ -293,18 +295,23 @@ class IntelliJConfig extends IdeConfig { final classElements = xml.findAllElements('root'); if (classElements.isNotEmpty) { final classElement = classElements.first; - final urlPath = Uri.parse(classElement - .getAttribute('url')! - .replaceAll( - RegExp(r'\$USER_HOME\$', caseSensitive: false), - config.homeDir.path, - ) - .replaceAll(RegExp(r'\$PROJECT_DIR\$', caseSensitive: false), - workspaceDir.path)) - .toFilePath() - .replaceAll(RegExp(r'^\\\\'), ''); - final dartSdkDir = - config.fileSystem.directory(urlPath).absolute.parent.parent; + final urlPath = Uri.parse( + classElement + .getAttribute('url')! + .replaceAll( + RegExp(r'\$USER_HOME\$', caseSensitive: false), + config.homeDir.path, + ) + .replaceAll( + RegExp(r'\$PROJECT_DIR\$', caseSensitive: false), + workspaceDir.path, + ), + ).toFilePath().replaceAll(RegExp(r'^\\\\'), ''); + final dartSdkDir = config.fileSystem + .directory(urlPath) + .absolute + .parent + .parent; if (dartSdkDir.childDirectory('bin').existsSync()) { intellijConfig.dartSdkDir = dartSdkDir.absolute; if (dartSdkDir.parent.basename == 'cache' && diff --git a/puro/lib/src/workspace/vscode.dart b/puro/lib/src/workspace/vscode.dart index a8a493b..ee0ef68 100644 --- a/puro/lib/src/workspace/vscode.dart +++ b/puro/lib/src/workspace/vscode.dart @@ -25,16 +25,10 @@ class VSCodeConfig extends IdeConfig { JsonEditor readSettings() { if (!settingsFile.existsSync()) { - return JsonEditor( - source: '{}', - indentLevel: 4, - ); + return JsonEditor(source: '{}', indentLevel: 4); } final source = settingsFile.readAsStringSync(); - return JsonEditor( - source: source.isEmpty ? '{}' : source, - indentLevel: 4, - ); + return JsonEditor(source: source.isEmpty ? '{}' : source, indentLevel: 4); } static const flutterSdkDirKey = 'dart.flutterSdkPath'; @@ -53,11 +47,9 @@ class VSCodeConfig extends IdeConfig { } if (dartSdkDir != null && dartSdkDir!.existsSync() && - !dartSdkDir! - .resolve() - .parent - .parent - .resolvedPathEquals(config.sharedCachesDir) && + !dartSdkDir!.resolve().parent.parent.resolvedPathEquals( + config.sharedCachesDir, + ) && !dotfile.hasPreviousDartSdk()) { dotfile.previousDartSdk = dartSdkDir!.path; changedDotfile = true; @@ -139,7 +131,8 @@ class VSCodeConfig extends IdeConfig { log.v('vscode workspaceDir: $workspaceDir'); if (workspaceDir == null) { return VSCodeConfig( - workspaceDir: findProjectDir(projectDir, '.idea') ?? + workspaceDir: + findProjectDir(projectDir, '.idea') ?? projectConfig.ensureParentProjectDir(), projectConfig: projectConfig, exists: false, @@ -153,11 +146,14 @@ class VSCodeConfig extends IdeConfig { if (vscodeConfig.settingsFile.existsSync() && vscodeConfig.settingsFile.lengthSync() > 0) { final editor = vscodeConfig.readSettings(); - final flutterSdkPathStr = - editor.query([flutterSdkDirKey])?.value.toJson(); + final flutterSdkPathStr = editor + .query([flutterSdkDirKey]) + ?.value + .toJson(); if (flutterSdkPathStr is String) { - vscodeConfig.flutterSdkDir = - config.fileSystem.directory(flutterSdkPathStr); + vscodeConfig.flutterSdkDir = config.fileSystem.directory( + flutterSdkPathStr, + ); } final dartSdkPathStr = editor.query([dartSdkDirKey])?.value.toJson(); if (dartSdkPathStr is String) { @@ -168,14 +164,14 @@ class VSCodeConfig extends IdeConfig { } } -Future isRunningInVscode({ - required Scope scope, -}) async { +Future isRunningInVscode({required Scope scope}) async { final processes = await getParentProcesses(scope: scope); - return processes.any((e) => - e.name == 'Code.exe' || - e.name == 'VSCode.exe' || - e.name == 'VSCodium.exe' || - e.name == 'code' || - e.name == 'codium'); + return processes.any( + (e) => + e.name == 'Code.exe' || + e.name == 'VSCode.exe' || + e.name == 'VSCodium.exe' || + e.name == 'code' || + e.name == 'codium', + ); } diff --git a/puro/test/json_edit_test.dart b/puro/test/json_edit_test.dart index 83c91bb..3912e8c 100644 --- a/puro/test/json_edit_test.dart +++ b/puro/test/json_edit_test.dart @@ -1,10 +1,7 @@ import 'package:puro/src/json_edit/editor.dart'; import 'package:test/test.dart'; -String _indentString( - String input, - String indent, -) { +String _indentString(String input, String indent) { return input.split('\n').map((e) => '$indent$e').join('\n'); } @@ -28,11 +25,7 @@ void testUpdate( expect(editor2.source, '[\n${_indentString(output, ' ')}\n]'); } -void testRemove( - String input, - List selectors, - String output, -) { +void testRemove(String input, List selectors, String output) { final editor = JsonEditor(source: input, indentLevel: 2); editor.remove(selectors); expect(editor.source, output); @@ -48,57 +41,37 @@ void testRemove( void main() { test('Expand empty map', () { - testUpdate( - '{}', - ['a'], - {'b': 'c'}, - '''{ + testUpdate('{}', ['a'], {'b': 'c'}, '''{ "a": { "b": "c" } -}''', - ); +}'''); }); test('Expand empty list', () { - testUpdate( - '[]', - [0], - {'b': 'c'}, - '''[ + testUpdate('[]', [0], {'b': 'c'}, '''[ { "b": "c" } -]''', - ); +]'''); }); test('Expand empty map with comment', () { - testUpdate( - '{ /*hi*/ }', - ['a'], - {'b': 'c'}, - '''{ + testUpdate('{ /*hi*/ }', ['a'], {'b': 'c'}, '''{ /*hi*/ "a": { "b": "c" } -}''', - ); +}'''); }); test('Expand empty list with comment', () { - testUpdate( - '[ /*hi*/ ]', - [0], - {'b': 'c'}, - '''[ + testUpdate('[ /*hi*/ ]', [0], {'b': 'c'}, '''[ /*hi*/ { "b": "c" } -]''', - ); +]'''); }); test('Expand empty map with literal', () { @@ -148,21 +121,11 @@ void main() { }); test('Dont expand map single-line siblings', () { - testUpdate( - '{"x": "y"}', - ['a'], - {'b': 'c'}, - '{"x": "y", "a": {"b": "c"}}', - ); + testUpdate('{"x": "y"}', ['a'], {'b': 'c'}, '{"x": "y", "a": {"b": "c"}}'); }); test('Dont expand list single-line siblings', () { - testUpdate( - '["a"]', - [1], - {'b': 'c'}, - '["a", {"b": "c"}]', - ); + testUpdate('["a"]', [1], {'b': 'c'}, '["a", {"b": "c"}]'); }); test('Dont expand map single-line siblings with comment', () { @@ -175,168 +138,103 @@ void main() { }); test('Dont expand list single-line siblings with comment', () { - testUpdate( - '["a" /*hi*/ ]', - [1], - {'b': 'c'}, - '["a" /*hi*/, {"b": "c"}]', - ); + testUpdate('["a" /*hi*/ ]', [1], {'b': 'c'}, '["a" /*hi*/, {"b": "c"}]'); }); test('Empty multiline map', () { - testUpdate( - '{\n}', - ['a'], - {'b': 'c'}, - '''{ + testUpdate('{\n}', ['a'], {'b': 'c'}, '''{ "a": { "b": "c" } -}''', - ); +}'''); }); test('Empty multiline list', () { - testUpdate( - '[\n]', - [0], - {'b': 'c'}, - '''[ + testUpdate('[\n]', [0], {'b': 'c'}, '''[ { "b": "c" } -]''', - ); +]'''); }); test('Empty multiline map with comment', () { - testUpdate( - '{/*a*/\n /*b*/}', - ['a'], - {'b': 'c'}, - '''{/*a*/ + testUpdate('{/*a*/\n /*b*/}', ['a'], {'b': 'c'}, '''{/*a*/ /*b*/ "a": { "b": "c" } -}''', - ); +}'''); }); test('Empty multiline list with comment', () { - testUpdate( - '[/*a*/\n /*b*/]', - [0], - {'b': 'c'}, - '''[/*a*/ + testUpdate('[/*a*/\n /*b*/]', [0], {'b': 'c'}, '''[/*a*/ /*b*/ { "b": "c" } -]''', - ); +]'''); }); test('Multiline map with siblings', () { - testUpdate( - '{\n "a": "b"\n}', - ['x'], - {'y': 'z'}, - '''{ + testUpdate('{\n "a": "b"\n}', ['x'], {'y': 'z'}, '''{ "a": "b", "x": { "y": "z" } -}''', - ); +}'''); }); test('Multiline list with siblings', () { - testUpdate( - '[\n "a"\n]', - [1], - {'b': 'c'}, - '''[ + testUpdate('[\n "a"\n]', [1], {'b': 'c'}, '''[ "a", { "b": "c" } -]''', - ); +]'''); }); test('Multiline map with siblings and comment', () { - testUpdate( - '{\n "a": "b" /*a*/ \n}', - ['x'], - {'y': 'z'}, - '''{ + testUpdate('{\n "a": "b" /*a*/ \n}', ['x'], {'y': 'z'}, '''{ "a": "b", /*a*/ "x": { "y": "z" } -}''', - ); +}'''); }); test('Multiline list with siblings and comment', () { - testUpdate( - '[\n "a" /*a*/ \n]', - [1], - {'b': 'c'}, - '''[ + testUpdate('[\n "a" /*a*/ \n]', [1], {'b': 'c'}, '''[ "a", /*a*/ { "b": "c" } -]''', - ); +]'''); }); test('Overwrite in map', () { - testUpdate( - '{"a": "b"}', - ['a'], - {'b': 'c'}, - '''{"a": {"b": "c"}}''', - ); + testUpdate('{"a": "b"}', ['a'], {'b': 'c'}, '''{"a": {"b": "c"}}'''); }); test('Overwrite in list', () { - testUpdate( - '["a"]', - [0], - {'a': 'b'}, - '''[{"a": "b"}]''', - ); + testUpdate('["a"]', [0], {'a': 'b'}, '''[{"a": "b"}]'''); }); test('Overwrite in multiline map', () { - testUpdate( - '{\n "a": "b",\n "c": "d"\n}', - ['c'], - ['deez'], - '''{ + testUpdate('{\n "a": "b",\n "c": "d"\n}', ['c'], ['deez'], '''{ "a": "b", "c": [ "deez" ] -}''', - ); +}'''); }); test('Overwrite in multiline list', () { - testUpdate( - '[\n "a",\n "b"\n]', - [1], - ['b'], - '''[ + testUpdate('[\n "a",\n "b"\n]', [1], ['b'], '''[ "a", [ "b" ] -]''', - ); +]'''); }); test('Create nested', () { @@ -356,115 +254,63 @@ void main() { }); test('Remove from map', () { - testRemove( - '{"a": "b"}', - ['a'], - '{}', - ); + testRemove('{"a": "b"}', ['a'], '{}'); }); test('Remove from list', () { - testRemove( - '["a"]', - [0], - '[]', - ); + testRemove('["a"]', [0], '[]'); }); test('Remove from map with comments', () { - testRemove( - '{ /*a*/ "a": "b" /*b*/ }', - ['a'], - '{}', - ); + testRemove('{ /*a*/ "a": "b" /*b*/ }', ['a'], '{}'); }); test('Remove from list with comments', () { - testRemove( - '[ /*a*/ "a" /*b*/ ]', - [0], - '[]', - ); + testRemove('[ /*a*/ "a" /*b*/ ]', [0], '[]'); }); test('Remove first from map', () { - testRemove( - '{"a": "b", "c": "d"}', - ['a'], - '{"c": "d"}', - ); + testRemove('{"a": "b", "c": "d"}', ['a'], '{"c": "d"}'); }); test('Remove first from list', () { - testRemove( - '["a", "b"]', - [0], - '["b"]', - ); + testRemove('["a", "b"]', [0], '["b"]'); }); test('Remove first from map with comments', () { - testRemove( - '{ /*a*/ "a": "b" /*b*/ , /*c*/ "c": "d"}', - ['a'], - '{/*c*/ "c": "d"}', - ); + testRemove('{ /*a*/ "a": "b" /*b*/ , /*c*/ "c": "d"}', [ + 'a', + ], '{/*c*/ "c": "d"}'); }); test('Remove first from list with comments', () { - testRemove( - '[ /*a*/ "a" /*b*/ , /*c*/ "b"]', - [0], - '[/*c*/ "b"]', - ); + testRemove('[ /*a*/ "a" /*b*/ , /*c*/ "b"]', [0], '[/*c*/ "b"]'); }); test('Remove last from map', () { - testRemove( - '{"a": "b", "c": "d"}', - ['c'], - '{"a": "b"}', - ); + testRemove('{"a": "b", "c": "d"}', ['c'], '{"a": "b"}'); }); test('Remove last from list', () { - testRemove( - '["a", "b"]', - [1], - '["a"]', - ); + testRemove('["a", "b"]', [1], '["a"]'); }); test('Remove last from map with comments', () { - testRemove( - '{"a": "b" /*a*/ , /*b*/ "c": "d" /*c*/}', - ['c'], - '{"a": "b" /*a*/}', - ); + testRemove('{"a": "b" /*a*/ , /*b*/ "c": "d" /*c*/}', [ + 'c', + ], '{"a": "b" /*a*/}'); }); test('Remove last from list with comments', () { - testRemove( - '["a" /*a*/ , /*b*/ "b" /*c*/]', - [1], - '["a" /*a*/]', - ); + testRemove('["a" /*a*/ , /*b*/ "b" /*c*/]', [1], '["a" /*a*/]'); }); test('Remove middle from map', () { - testRemove( - '{"a": "b", "c": "d", "e": "f"}', - ['c'], - '{"a": "b", "e": "f"}', - ); + testRemove('{"a": "b", "c": "d", "e": "f"}', ['c'], '{"a": "b", "e": "f"}'); }); test('Remove middle from list', () { - testRemove( - '["a", "b", "c"]', - [1], - '["a", "c"]', - ); + testRemove('["a", "b", "c"]', [1], '["a", "c"]'); }); test('Remove middle from map with comments', () { @@ -476,11 +322,9 @@ void main() { }); test('Remove middle from list with comments', () { - testRemove( - '["a" /*a*/ , /*b*/ "b" /*c*/ , /*d*/ "c"]', - [1], - '["a" /*a*/ , /*d*/ "c"]', - ); + testRemove('["a" /*a*/ , /*b*/ "b" /*c*/ , /*d*/ "c"]', [ + 1, + ], '["a" /*a*/ , /*d*/ "c"]'); }); test('Remove from multiline map', () { diff --git a/website/docs/reference/manual.md b/website/docs/reference/manual.md index 09a7f0d..510d05c 100644 --- a/website/docs/reference/manual.md +++ b/website/docs/reference/manual.md @@ -193,3 +193,18 @@ We can manually delete unused caches with the `puro gc` command: $> puro gc [✓] Cleaned up caches and reclaimed 2.7GB ``` + +### Preparing artifacts + +Use `puro prepare` to download Flutter artifacts ahead of time so the first build on a new +or freshly upgraded environment does not need to fetch them on demand: + +``` +$> puro prepare master +[✓] Prepared environment `master` (default platforms (android, ios, macos, web)) +``` + +Add `--all-platforms` to cache everything Flutter supports, or pass `--platform` multiple +times (for example `--platform android --platform web`) to tailor the download to the +projects you build most often. Include `--force` if you want to refresh artifacts even +when they are already present. From db1fc46fe853db5091b658d47055f339133b6f4d Mon Sep 17 00:00:00 2001 From: vonarian Date: Sun, 5 Oct 2025 10:18:28 +0330 Subject: [PATCH 4/5] Clean-up lints --- puro/lib/src/cli.dart | 2 +- puro/lib/src/commands/prepare.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/puro/lib/src/cli.dart b/puro/lib/src/cli.dart index de7ec1e..d673ee6 100644 --- a/puro/lib/src/cli.dart +++ b/puro/lib/src/cli.dart @@ -21,11 +21,11 @@ import 'commands/internal_generate_ast_parser.dart'; import 'commands/internal_generate_docs.dart'; import 'commands/ls_versions.dart'; import 'commands/prefs.dart'; +import 'commands/prepare.dart'; import 'commands/pub.dart'; import 'commands/puro_install.dart'; import 'commands/puro_uninstall.dart'; import 'commands/puro_upgrade.dart'; -import 'commands/prepare.dart'; import 'commands/repl.dart'; import 'commands/run.dart'; import 'commands/version.dart'; diff --git a/puro/lib/src/commands/prepare.dart b/puro/lib/src/commands/prepare.dart index 5e76bc0..0ddc33b 100644 --- a/puro/lib/src/commands/prepare.dart +++ b/puro/lib/src/commands/prepare.dart @@ -78,7 +78,7 @@ class PrepareCommand extends PuroCommand { : platforms.join(', ')); return BasicMessageResult( - 'Prepared environment `${environment.name}` (${platformSummary}${force ? ', forced' : ''})', + 'Prepared environment `${environment.name}` ($platformSummary${force ? ', forced' : ''})', ); } } From 6aada51ed74f28daea9b4a810a8c17dbaed92489 Mon Sep 17 00:00:00 2001 From: vonarian <68429735+Vonarian@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:47:30 +0330 Subject: [PATCH 5/5] chore: bump Dart SDK version - Bump minimum Dart SDK from 3.9.2 to 3.10.0. --- puro/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puro/pubspec.yaml b/puro/pubspec.yaml index f3ee841..ddc39a1 100644 --- a/puro/pubspec.yaml +++ b/puro/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/pingbird/puro homepage: https://puro.dev environment: - sdk: '>=3.9.2 <4.0.0' + sdk: ">=3.10.0 <4.0.0" dependencies: analyzer: ^7.4.5