generateForAnnotatedElement method

  1. @override
FutureOr<String> generateForAnnotatedElement(
  1. Element element,
  2. ConstantReader annotation,
  3. BuildStep buildStep
)

Implement to return source code to generate for element.

This method is invoked based on finding elements annotated with an instance of T. The annotation is provided as a ConstantReader.

Supported return values include a single String or multiple String instances within an Iterable or Stream. It is also valid to return a Future of String, Iterable, or Stream. When multiple values are returned through an iterable or stream they will be deduplicated. Typically each value will be an independent unit of code and the deduplication prevents re-defining the same member multiple times. For example if multiple annotated elements may need a specific utility method available it can be output for each one, and the single deduplicated definition can be shared.

Implementations should return null when no content is generated. Empty or whitespace-only String instances are also ignored.

Implementation

@override
FutureOr<String> generateForAnnotatedElement(
  Element element,
  ConstantReader annotation,
  BuildStep buildStep,
) async {
  if (element is! ClassElement) {
    throw InvalidGenerationSourceError(
      'ShellScripts annotation can only be applied to classes.',
      element: element,
    );
  }

  final scriptsPath = annotation.read('scriptsPath').stringValue;
  final className = '${element.name}Scripts';
  final enableParameters = annotation.read('enableParameters').boolValue;
  final methodPrefix = annotation.read('methodPrefix').stringValue;

  // Get the list of methods with the @ShellScript annotation
  final annotatedMethods = _getAnnotatedMethods(element);

  if (annotatedMethods.isEmpty) {
    throw InvalidGenerationSourceError(
      'No methods with @ShellScript annotation found in class ${element.name}',
      element: element,
    );
  }

  // Check for the existence of script files
  final validMethods = <MethodElement>[];
  for (final method in annotatedMethods) {
    final scriptAnnotation = _getShellScriptAnnotation(method);
    if (scriptAnnotation != null) {
      final fileName = scriptAnnotation.read('fileName').stringValue;
      final fullPath = path.join(scriptsPath, fileName);

      if (await File(fullPath).exists()) {
        validMethods.add(method);
      } else {
        log.warning(
            'Script file not found: $fullPath for method ${method.name}');
      }
    }
  }

  if (validMethods.isEmpty) {
    throw InvalidGenerationSourceError(
      'No valid script files found for annotated methods in class ${element.name}',
      element: element,
    );
  }

  final buffer = StringBuffer();
  _generateClassHeader(buffer, className);

  // Generate constants for all valid methods
  for (final method in validMethods) {
    final scriptAnnotation = _getShellScriptAnnotation(method)!;
    final fileName = scriptAnnotation.read('fileName').stringValue;
    final fullPath = path.join(scriptsPath, fileName);

    await _generateScriptConstant(
        buffer, fullPath, method.name, methodPrefix);
  }

  // Generate accessor methods
  for (final method in validMethods) {
    await _generateAccessMethod(
      buffer,
      method,
      methodPrefix,
      enableParameters,
    );
  }

  if (enableParameters) {
    buffer.writeln(ShellScriptParameterizer.generateBuildScriptMethod());
  }

  buffer.writeln('}');

  return buffer.toString();
}