Skip to content

Commit

Permalink
Add documentation generation for nitrogen
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Jul 1, 2024
1 parent 192fff4 commit ab95834
Show file tree
Hide file tree
Showing 29 changed files with 565 additions and 121 deletions.
4 changes: 4 additions & 0 deletions nitrogen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## 0.3.0+1
* Fix nitrogen erroneously importing Flutter and causing it to crash.

## 0.3.0
* Add configuration for documentation generation.
* Add configuration for output locations.
* Move configuration from `pubspec.yaml` to `build.yaml`.
* Remove generation of `flutter_extension`.
Expand Down
5 changes: 5 additions & 0 deletions nitrogen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ targets:
nitrogen:
options:
package: true
docs: false
prefix: 'MyPrefix'
key: file-name
assets:
Expand All @@ -99,6 +100,10 @@ targets:
Optional. Defaults to `false`. Controls whether to generate assets as a package dependency. This should be `true` if
you're bundling assets for other projects to use, i.e. [forui-assets](https://github.com/forus-labs/forui).

### `docs`

Optional. Defaults to `true`. Controls whether to generate docs alongside the classes.

### `prefix`

Optional. Defaults to an empty string. Controls generated classes' prefixes. Given 'MyPrefix', the generated classes will
Expand Down
1 change: 1 addition & 0 deletions nitrogen/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ builders:
defaults:
options:
package: false
docs: true
prefix: ""
key: "file-name"
assets:
Expand Down
15 changes: 11 additions & 4 deletions nitrogen/lib/nitrogen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,30 @@ class NitrogenBuilder extends Builder {
var fallbackTheme = assets;
for (final segment in split(fallback).skip(1)) {
themes = fallbackTheme;
fallbackTheme = fallbackTheme.children[segment]! as AssetDirectory;

final nested = fallbackTheme.children[segment];
if (nested == null) {
log.severe('Unable to find path to fallback theme, "$fallback". Did you specify it under flutter.assets in your pubspec.yaml?');
return;
}

fallbackTheme = nested as AssetDirectory;
}

await buildStep.writeAsString(
assetsOutput,
AssetGenerator(configuration.prefix, assets, { themes }).generate(),
AssetGenerator(configuration.prefix, assets, { themes }, docs: configuration.docs).generate(),
);

await buildStep.writeAsString(
AssetId(buildStep.inputId.package, output),
ThemeGenerator(configuration.prefix, themes, fallbackTheme).generate(),
ThemeGenerator(configuration.prefix, themes, fallbackTheme, docs: configuration.docs).generate(),
);

} else {
await buildStep.writeAsString(
assetsOutput,
AssetGenerator(configuration.prefix, assets, {}).generate(),
AssetGenerator(configuration.prefix, assets, {}, docs: configuration.docs).generate(),
);
}

Expand Down
14 changes: 10 additions & 4 deletions nitrogen/lib/src/configuration/build_configuration.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import 'package:build/build.dart';
import 'package:meta/meta.dart';

import 'package:nitrogen/src/configuration/key.dart';
import 'package:nitrogen/src/nitrogen_exception.dart';

/// Nitrogen's build.yaml configuration.
final class BuildConfiguration {

/// The Nitrogen configuration's valid keys.
static const keys = { 'package', 'prefix', 'key', 'assets', 'themes' };
static const keys = { 'package', 'docs', 'prefix', 'key', 'assets', 'themes' };

/// Lints the pubspec.
static void lint(Map<dynamic, dynamic> configuration) {
Expand All @@ -30,7 +29,8 @@ final class BuildConfiguration {
/// Parses the assets configuration.
@visibleForTesting
static ({String output,}) parseAssets(Map<dynamic, dynamic> configuration) {
switch (configuration) {
final copy = <dynamic, dynamic>{ 'output': 'lib/src/assets.nitrogen.dart' }..addAll(configuration);
switch (copy) {
case { 'output': final String output }:
return (output: output);

Expand All @@ -43,7 +43,8 @@ final class BuildConfiguration {
/// Parses the themes configuration.
@visibleForTesting
static ({String fallback, String output})? parseThemes(Map<dynamic, dynamic> configuration) {
switch (configuration) {
final copy = <dynamic, dynamic>{ 'output': 'lib/src/asset_themes.nitrogen.dart' }..addAll(configuration);
switch (copy) {
case { 'fallback': final String fallback, 'output': final String output }:
return (fallback: fallback, output: output);

Expand All @@ -59,6 +60,9 @@ final class BuildConfiguration {
/// Whether to generate assets for a package.
final bool package;

/// Whether to generate documentation.
final bool docs;

/// The prefix for generated classes.
final String prefix;

Expand All @@ -74,6 +78,7 @@ final class BuildConfiguration {
/// Parses the build configuration.
factory BuildConfiguration.parse(Map<dynamic, dynamic> configuration) => BuildConfiguration(
package: configuration['package'],
docs: configuration['docs'] ?? true,
prefix: configuration['prefix'],
key: Key.parse(configuration['key']),
assets: parseAssets(configuration['assets']),
Expand All @@ -83,6 +88,7 @@ final class BuildConfiguration {
/// Creates a [BuildConfiguration].
BuildConfiguration({
required this.package,
required this.docs,
required this.prefix,
required this.key,
required this.assets,
Expand Down
5 changes: 5 additions & 0 deletions nitrogen/lib/src/configuration/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final class Configuration {
/// Parses the configuration from the project's pubspec.yaml.
factory Configuration.merge(BuildConfiguration configuration, YamlMap pubspec) => Configuration(
package: parsePackage(pubspec.nodes['name'], enabled: configuration.package),
docs: configuration.docs,
prefix: configuration.prefix,
key: configuration.key,
assets: configuration.assets,
Expand Down Expand Up @@ -53,6 +54,9 @@ final class Configuration {
/// The package name.
final String? package;

/// True if dart docs should be generated.
final bool docs;

/// The class prefix.
final String prefix;

Expand All @@ -71,6 +75,7 @@ final class Configuration {
/// Creates a [Configuration].
Configuration({
required this.package,
required this.docs,
required this.prefix,
required this.key,
required this.assets,
Expand Down
2 changes: 1 addition & 1 deletion nitrogen/lib/src/file_system.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:collection';

import 'package:nitrogen_types/nitrogen_types.dart';
import 'package:nitrogen_types/assets.dart';
import 'package:path/path.dart';

/// An entity in the file system.
Expand Down
51 changes: 47 additions & 4 deletions nitrogen/lib/src/generators/asset_generator.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:code_builder/code_builder.dart';
import 'package:nitrogen_types/nitrogen_types.dart';
import 'package:meta/meta.dart' show visibleForOverriding;
import 'package:nitrogen_types/assets.dart';
import 'package:sugar/sugar.dart';

import 'package:nitrogen/src/file_system.dart';
Expand All @@ -13,10 +14,11 @@ class AssetGenerator {
final Set<AssetDirectory> _excluded;

/// Creates a [AssetGenerator].
AssetGenerator(String prefix, this._assets, this._excluded):
AssetGenerator(String prefix, this._assets, this._excluded, {required bool docs}):
_standardClass = AssetClass(
directories: AssetDirectoryExpressions(prefix),
excluded: _excluded,
docs: docs,
);

/// Generates basic asset classes.
Expand Down Expand Up @@ -48,15 +50,45 @@ class AssetClass extends BasicAssetClass {
static final assetType = refer('Asset', 'package:nitrogen_types/nitrogen_types.dart');

/// Creates a [AssetClass].
const AssetClass({required super.directories, super.excluded, super.files});
const AssetClass({required super.directories, required super.docs, super.excluded, super.files});

/// Generates the documentation for [directory].
@visibleForOverriding
String generateDocs(AssetDirectory directory) => '''
/// Contains the assets and nested directories in the `${directory.path.join('/')}` directory.
///
/// Besides the assets and nested directories, it also provides a [contents] for querying the assets in the current
/// directory.
///
/// To convert an asset into a widget, [call the asset like a function](https://dart.dev/language/callable-objects].
/// ```dart
/// final widget = ${directories.type(directory).symbol!}.path.to.asset();
/// ```
///
/// The `call(...)` functions are provided by extensions. By default, only the [ImageAsset] extension is bundled.
///
/// 3rd party packages are supported via 'extension' packages. `extension` packages contain an `extension` that provide a
/// `call(...)` function that transforms an `Asset` into a 3rd party type.
///
/// | Type | Package | Extension Package | Version |
/// |-------------------|---------------|------------------------|----------------------------------------------------------------------------------------------------------------|
/// | SVG images | `flutter_svg` | `nitrogen_flutter_svg` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_flutter_svg)](https://pub.dev/packages/nitrogen_flutter_svg) |
/// | Lottie animations | `lottie` | `nitrogen_lottie` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_lottie)](https://pub.dev/packages/nitrogen_lottie) |''';

/// Generates a basic class that contains only nested folders and assets.
@override
ClassBuilder generate(AssetDirectory directory, {bool static = false}) => super.generate(directory, static: static)
..docs.addAll([
if (docs)
generateDocs(directory),
])
..methods.add(_contents(directory, static: static));

/// Returns a
Method _contents(AssetDirectory directory, {bool static = false}) => Method((builder) => builder
..docs.addAll([
if (docs)
'/// The contents of this directory.',
])
..static = static
..returns = TypeReference((builder) => builder..symbol = 'Map'..types.addAll([refer('String'), assetType]))
..type = MethodType.getter
Expand All @@ -81,10 +113,13 @@ class BasicAssetClass {
final AssetDirectoryExpressions directories;
/// The file expressions.
final AssetFileExpressions files;
/// True if dart docs should be generated.
final bool docs;

/// Creates a [BasicAssetClass].
const BasicAssetClass({
required this.directories,
required this.docs,
this.excluded = const {},
this.files = const AssetFileExpressions(),
});
Expand All @@ -103,6 +138,10 @@ class BasicAssetClass {

/// Returns a getter for the nested [directory].
Method _assetDirectoryGetter(AssetDirectory directory, {bool static = false}) => Method((builder) => builder
..docs.addAll([
if (docs)
'/// The `${directory.path.join('/')}` directory.'
])
..static = static
..returns = directories.type(directory)
..type = MethodType.getter
Expand All @@ -113,6 +152,10 @@ class BasicAssetClass {

/// Returns a getter for the nested [file].
Method _assetFileGetter(AssetFile file, {bool static = false}) => Method((builder) => builder
..docs.addAll([
if (docs)
'/// The `${file.path.join('/')}`.'
])
..static = static
..returns = files.type(file)
..type = MethodType.getter
Expand Down
81 changes: 73 additions & 8 deletions nitrogen/lib/src/generators/theme_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ class ThemeGenerator {
static int _name((String, int) a, (String, int) b) => a.$1.compareTo(b.$1);

final ThemeExtension _extension;
final AssetClass _fallbackClass;
final AssetClass _themeSubclass;
final FallbackAssetClass _fallbackClass;
final FallbackAssetClass _themeSubclass;
final ThemeClass _themeClass;
final AssetDirectory _themes;
final AssetDirectory _fallbackTheme;

/// Creates a [ThemeGenerator].
ThemeGenerator(String prefix, this._themes, this._fallbackTheme):
ThemeGenerator(String prefix, this._themes, this._fallbackTheme, {required bool docs}):
_extension = ThemeExtension(prefix),
_fallbackClass = AssetClass(directories: FallbackAssetDirectoryExpressions(prefix, _fallbackTheme)),
_themeSubclass = AssetClass(directories: ThemeAssetDirectoryExpressions(prefix, _themes)),
_themeClass = ThemeClass(ThemeAssetDirectoryExpressions(prefix, _themes));
_fallbackClass = FallbackAssetClass(directories: FallbackAssetDirectoryExpressions(prefix, _fallbackTheme), docs: docs),
_themeSubclass = FallbackAssetClass(directories: ThemeAssetDirectoryExpressions(prefix, _themes), docs: docs),
_themeClass = ThemeClass(ThemeAssetDirectoryExpressions(prefix, _themes), docs: docs);

/// Generates themed asset classes.
String generate() {
Expand Down Expand Up @@ -103,21 +103,83 @@ class ThemeExtension {

}

/// Contains functions for generating a standard class representation of a directory with theme-specific documentation.
class FallbackAssetClass extends AssetClass {

/// Creates a [FallbackAssetClass].
FallbackAssetClass({required super.directories, required super.docs});

@override
String generateDocs(AssetDirectory directory) => '''
/// The fallback theme's assets.
///
/// Contains the assets in the `${directory.path.join('/')}` directory that serve as a fallback for when a theme does not
/// contain those specific assets.
///
/// To convert an asset into a widget, [call the asset like a function](https://dart.dev/language/callable-objects].
/// ```dart
/// final widget = ${directories.type(directory).symbol!}.path.to.asset();
/// ```
///
/// The `call(...)` functions are provided by extensions. By default, only the [ImageAsset] extension is bundled.
///
/// 3rd party packages are supported via 'extension' packages. `extension` packages contain an `extension` that provide a
/// `call(...)` function that transforms an `Asset` into a 3rd party type.
///
/// | Type | Package | Extension Package | Version |
/// |-------------------|---------------|------------------------|----------------------------------------------------------------------------------------------------------------|
/// | SVG images | `flutter_svg` | `nitrogen_flutter_svg` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_flutter_svg)](https://pub.dev/packages/nitrogen_flutter_svg) |
/// | Lottie animations | `lottie` | `nitrogen_lottie` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_lottie)](https://pub.dev/packages/nitrogen_lottie) |''';

}

/// Contains functions for generating a theme class representation of a directory.
class ThemeClass {

final ThemeAssetDirectoryExpressions _directories;
final BasicAssetClass _assets;
final bool _docs;

/// Creates a [ThemeClass].
ThemeClass(AssetDirectoryExpressions directories): _assets = BasicAssetClass(directories: directories);
ThemeClass(ThemeAssetDirectoryExpressions directories, {required bool docs}):
_directories = directories,
_assets = BasicAssetClass(directories: directories, docs: docs),
_docs = docs;

/// Generates a theme class that extends [fallback].
ClassBuilder generate(AssetDirectory directory, Class fallback) => _assets.generate(directory)
..docs.addAll([
if (_docs)
'''
/// A `${_directories.theme(directory)}` theme's directory.
///
/// Contains the assets in the `${directory.path.join('/')}` directory that serve as a fallback for when a theme does not
/// contain those specific assets.
///
/// To convert an asset into a widget, [call the asset like a function](https://dart.dev/language/callable-objects].
/// ```dart
/// final widget = ${_directories.type(directory).symbol!}.path.to.asset();
/// ```
///
/// The `call(...)` functions are provided by extensions. By default, only the [ImageAsset] extension is bundled.
///
/// 3rd party packages are supported via 'extension' packages. `extension` packages contain an `extension` that provide a
/// `call(...)` function that transforms an `Asset` into a 3rd party type.
///
/// | Type | Package | Extension Package | Version |
/// |-------------------|---------------|------------------------|----------------------------------------------------------------------------------------------------------------|
/// | SVG images | `flutter_svg` | `nitrogen_flutter_svg` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_flutter_svg)](https://pub.dev/packages/nitrogen_flutter_svg) |
/// | Lottie animations | `lottie` | `nitrogen_lottie` | [![Pub Dev](https://img.shields.io/pub/v/nitrogen_lottie)](https://pub.dev/packages/nitrogen_lottie) |''',
])
..extend = refer(fallback.name)
..modifier = ClassModifier.final$
..methods.add(_contents(directory, fallback));

Method _contents(AssetDirectory directory, Class fallback, {bool static = false}) => Method((builder) => builder
..docs.addAll([
if (_docs)
'/// The contents of this directory.',
])
..static = static
..returns = TypeReference((builder) => builder..symbol = 'Map'..types.addAll([refer('String'), AssetClass.assetType]))
..type = MethodType.getter
Expand Down Expand Up @@ -161,7 +223,10 @@ class ThemeAssetDirectoryExpressions extends AssetDirectoryExpressions {
@override
Reference type(AssetDirectory directory) {
final path = directory.path.sublist(_themes.path.length + 1);
return refer('${path.isEmpty ? '' : r'$'}${directory.path[_themes.path.length].toPascalCase()}${prefix}ThemeAssets${path.join('-').toPascalCase()}');
return refer('${path.isEmpty ? '' : r'$'}${theme(directory).toPascalCase()}${prefix}ThemeAssets${path.join('-').toPascalCase()}');
}

/// The theme.
String theme(AssetDirectory directory) => directory.path[_themes.path.length];

}
Loading

0 comments on commit ab95834

Please sign in to comment.