| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| import 'package:json_schema/json_schema.dart'; |
| import 'package:json_syntax_generator/json_syntax_generator.dart'; |
| import 'package:native_test_helpers/native_test_helpers.dart'; |
| |
| void main(List<String> args) { |
| final packageRoot = findPackageRoot('pub_formats'); |
| |
| final stopwatch = Stopwatch()..start(); |
| final parser = ArgParser() |
| ..addFlag( |
| 'set-exit-if-changed', |
| negatable: false, |
| help: 'Return a non-zero exit code if any files were changed.', |
| ) |
| ..addFlag( |
| 'dump', |
| abbr: 'd', |
| negatable: false, |
| help: 'Dump analyzed schema to .g.txt file.', |
| ); |
| final argResults = parser.parse(args); |
| |
| final setExitIfChanged = argResults['set-exit-if-changed'] as bool; |
| final dumpAnalyzedSchema = argResults['dump'] as bool; |
| var generatedCount = 0; |
| var changedCount = 0; |
| |
| for (final name in [ |
| 'package_config', |
| 'package_graph', |
| 'pubspec_lock', |
| 'pubspec', |
| ]) { |
| final schemaFile = File.fromUri( |
| packageRoot.resolve('doc/schema/${name}.schema.json'), |
| ); |
| final schemaJson = jsonDecode(schemaFile.readAsStringSync()); |
| final schema = JsonSchema.create(schemaJson); |
| |
| final analyzedSchema = SchemaAnalyzer( |
| schema, |
| nameOverrides: {'path': 'path\$'}, |
| publicSetters: ['PubspecYamlFile'], |
| publicValidators: ['PubspecYamlFile'], |
| ).analyze(); |
| final textDumpFile = File.fromUri( |
| packageRoot.resolve('lib/src/${name}_syntax.g.txt'), |
| ); |
| final newTextDumpContent = analyzedSchema.toString(); |
| if (dumpAnalyzedSchema) { |
| var oldTextDumpContent = ''; |
| if (textDumpFile.existsSync()) { |
| oldTextDumpContent = textDumpFile.readAsStringSync(); |
| } |
| if (oldTextDumpContent != newTextDumpContent) { |
| textDumpFile.writeAsStringSync(newTextDumpContent); |
| print('Generated ${textDumpFile.uri}'); |
| changedCount += 1; |
| } |
| generatedCount += 1; |
| } else if (textDumpFile.existsSync()) { |
| textDumpFile.deleteSync(); |
| print('Deleted ${textDumpFile.uri}'); |
| changedCount += 1; |
| } |
| |
| final output = SyntaxGenerator( |
| analyzedSchema, |
| header: ''' |
| // This file is generated, do not edit. |
| // File generated by pkgs/pub_formats/tool/generate.dart. |
| // Must be rerun when pkgs/pub_formats/doc/schema/ is modified. |
| ''', |
| ).generate(); |
| final outputUri = packageRoot.resolve('lib/src/${name}_syntax.g.dart'); |
| final outputFile = File.fromUri(outputUri); |
| var oldOutputContent = ''; |
| if (outputFile.existsSync()) { |
| oldOutputContent = outputFile.readAsStringSync(); |
| } |
| |
| if (oldOutputContent != output) { |
| outputFile.writeAsStringSync(output); |
| } |
| |
| Process.runSync(Platform.executable, ['format', outputUri.toFilePath()]); |
| |
| final newOutputContent = outputFile.readAsStringSync(); |
| |
| final newContentNormalized = newOutputContent.replaceAll('\r\n', '\n'); |
| final oldContentNormalized = oldOutputContent.replaceAll('\r\n', '\n'); |
| if (newContentNormalized != oldContentNormalized) { |
| print('Generated $outputUri'); |
| changedCount += 1; |
| } |
| generatedCount += 1; |
| } |
| |
| stopwatch.stop(); |
| final duration = stopwatch.elapsedMilliseconds / 1000.0; |
| print( |
| 'Generated $generatedCount files ($changedCount changed) in ' |
| '${duration.toStringAsFixed(2)} seconds.', |
| ); |
| if (setExitIfChanged && changedCount > 0) { |
| exit(1); |
| } |
| } |