diff --git a/packages/material_ui/pubspec.yaml b/packages/material_ui/pubspec.yaml index 70489e1f2f61..fab783e33bd1 100644 --- a/packages/material_ui/pubspec.yaml +++ b/packages/material_ui/pubspec.yaml @@ -11,6 +11,7 @@ environment: workspace: - example + - tool/m3e_gen_defaults dependencies: cupertino_ui: diff --git a/packages/material_ui/tool/m3e_gen_defaults/README.md b/packages/material_ui/tool/m3e_gen_defaults/README.md new file mode 100644 index 000000000000..f0d300074fe4 --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/README.md @@ -0,0 +1,24 @@ +## Token Defaults Generator + +Script that generates component theme data defaults based on token data. + +## Usage +Run this program from the root of the git repository: +```sh +dart packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart [-v] +``` + +This updates generated component theming files under +`packages/material_ui/lib/src/m3e/generated`. + +## Templates + +There is a template file for every component that needs defaults from the token +database. These templates are implemented as subclasses of `TokenTemplate`. + +Templates need to override the `generateContents` method to provide the +generated code block as a string. + +## Tokens + +Tokens are stored in `data/`, and are sourced from an internal Google database. \ No newline at end of file diff --git a/packages/material_ui/tool/m3e_gen_defaults/analysis_options.yaml b/packages/material_ui/tool/m3e_gen_defaults/analysis_options.yaml new file mode 100644 index 000000000000..1fb98de01a0b --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/analysis_options.yaml @@ -0,0 +1,4 @@ +include: ../../../../analysis_options.yaml + +formatter: + page_width: 100 diff --git a/packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart b/packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart new file mode 100644 index 000000000000..42e66881f10a --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// ## Usage +// +// Run from the root of flutter/packages: +// +// ``` +// dart packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart [-v] +// ``` + +import 'package:args/args.dart'; + +// TODO(elliette): Import template files. +// import '../templates/x_template.dart'; + +Future main(List args) async { + // Parse arguments + final parser = ArgParser(); + parser.addFlag('verbose', abbr: 'v', help: 'Enable verbose output', negatable: false); + final ArgResults argResults = parser.parse(args); + // TODO(elliette): Add token logger when verbose flag is used. + // ignore: unused_local_variable + final verbose = argResults['verbose'] as bool; + // TODO(elliette): Invoke template generators. + // const XTemplate().generateFile(verbose: verbose); +} diff --git a/packages/material_ui/tool/m3e_gen_defaults/pubspec.yaml b/packages/material_ui/tool/m3e_gen_defaults/pubspec.yaml new file mode 100644 index 000000000000..2fdde7945bad --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/pubspec.yaml @@ -0,0 +1,15 @@ +name: gen_defaults +description: A command line script to generate Material component defaults from the token database. +version: 1.0.0 + +environment: + sdk: ^3.10.0-0 + +resolution: workspace + +dependencies: + args: any + +dev_dependencies: + path: any + test: any diff --git a/packages/material_ui/tool/m3e_gen_defaults/templates/template.dart b/packages/material_ui/tool/m3e_gen_defaults/templates/template.dart new file mode 100644 index 000000000000..8d4e1fc12df1 --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/templates/template.dart @@ -0,0 +1,78 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +abstract class TokenTemplate { + const TokenTemplate(); + + /// The name of the template, which corresponds to the target file name. + /// E.g., 'typography' for generating 'typography_defaults.g.dart'. + String get name; + + static const String copyrightHeader = ''' +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +'''; + + static const String headerComment = ''' + +// Do not edit by hand. The code is generated from data in the Material +// Design token database by the script: +// packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart. + +// dart format off +'''; + + static const String footerComment = ''' +// dart format on +'''; + + String generateContents(); + + void generateFile({bool verbose = false}) { + final fileName = '$materialLib/${name}_defaults.g.dart'; + if (verbose) { + stdout.writeln('Generating file: $fileName'); + stdout.writeln('Target parent file name: $name.dart'); + } + final file = File(fileName); + if (!file.existsSync()) { + if (verbose) { + stdout.writeln('File does not exist, creating it.'); + } + file.createSync(recursive: true); + } + + final parentName = '$name.dart'; + + if (verbose) { + stdout.writeln('Generating contents...'); + } + final buffer = StringBuffer(); + buffer.write(copyrightHeader); + buffer.write(headerComment); + buffer.write("part of '../$parentName';\n\n"); + buffer.write(generateContents()); + buffer.write(footerComment); + + if (verbose) { + stdout.writeln('Writing generated contents to $fileName...'); + } + file.writeAsStringSync(buffer.toString()); + if (verbose) { + stdout.writeln('Done generating $fileName.'); + } + } + + String get materialLib { + const packagePath = 'packages/material_ui'; + const relativeOutputPath = 'lib/src/m3e/generated'; + if (Directory(packagePath).existsSync()) { + return '$packagePath/$relativeOutputPath'; + } + return relativeOutputPath; + } +} diff --git a/packages/material_ui/tool/m3e_gen_defaults/test/gen_defaults_test.dart b/packages/material_ui/tool/m3e_gen_defaults/test/gen_defaults_test.dart new file mode 100644 index 000000000000..7451c5e1c4b2 --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/test/gen_defaults_test.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:test/test.dart'; +import 'test_fixtures/button_template.dart'; + +void main() { + group('TokenTemplate', () { + test( + 'Templates will generate a part file ending in _defaults.g.dart with correct parent reference', + () { + final Directory tempDir = Directory.systemTemp.createTempSync('gen_defaults'); + try { + final template = ButtonTemplate(tempDir.path); + template.generateFile(); + + final file = File('${tempDir.path}/button_defaults.g.dart'); + expect(file.readAsStringSync(), ''' +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Do not edit by hand. The code is generated from data in the Material +// Design token database by the script: +// packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart. + +// dart format off +part of '../button.dart'; + +abstract final class _ButtonDefaults { + static const double height = 40.0; + static const double borderRadius = 8.0; +} +// dart format on +'''); + } finally { + tempDir.deleteSync(recursive: true); + } + }, + ); + + test('Templates will completely overwrite any previous code', () { + final Directory tempDir = Directory.systemTemp.createTempSync('gen_defaults'); + try { + // Seed the file with pre-existing random text. + final file = File('${tempDir.path}/button_defaults.g.dart'); + file.writeAsStringSync('Pre-existing random text.'); + + final templateNew = ButtonTemplate(tempDir.path); + templateNew.generateFile(); + expect(file.readAsStringSync(), ''' +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Do not edit by hand. The code is generated from data in the Material +// Design token database by the script: +// packages/material_ui/tool/m3e_gen_defaults/bin/gen_defaults.dart. + +// dart format off +part of '../button.dart'; + +abstract final class _ButtonDefaults { + static const double height = 40.0; + static const double borderRadius = 8.0; +} +// dart format on +'''); + } finally { + tempDir.deleteSync(recursive: true); + } + }); + }); +} diff --git a/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_template.dart b/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_template.dart new file mode 100644 index 000000000000..ed5742162c94 --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_template.dart @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import '../../templates/template.dart'; +import 'button_token_data.dart'; + +class ButtonTemplate extends TokenTemplate { + ButtonTemplate(this.customMaterialLib); + + final String customMaterialLib; + + @override + String get name => 'button'; + + @override + String get materialLib => customMaterialLib; + + @override + String generateContents() { + return ''' +abstract final class _ButtonDefaults { + static const double height = ${TokenButton.height}; + static const double borderRadius = ${TokenButton.borderRadius}; +} +'''; + } +} diff --git a/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_token_data.dart b/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_token_data.dart new file mode 100644 index 000000000000..539da016e907 --- /dev/null +++ b/packages/material_ui/tool/m3e_gen_defaults/test/test_fixtures/button_token_data.dart @@ -0,0 +1,8 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +abstract final class TokenButton { + static const double height = 40.0; + static const double borderRadius = 8.0; +}