populate method

Map<String, String?> populate()

Implementation

Map<String, String?> populate() {
  final variables = <String, String?>{};

  final projectRoot = pubspecYaml.nearest();
  variables[Vars.projectRoot] =
      projectRoot == null ? null : path.dirname(projectRoot);

  final scriptsRoot = scriptsYaml.nearest();
  variables[Vars.scriptsRoot] =
      scriptsRoot == null ? null : path.dirname(scriptsRoot);

  variables[Vars.cwd] = cwd.path;

  final definedVariables = scriptsYaml.variables();
  for (final MapEntry(:key, :value) in (definedVariables ?? {}).entries) {
    if (value is! String) {
      logger.warn('Variable $key is not a string');
      continue;
    }

    if (Vars.values.contains(key)) {
      logger.warn('Variable $key is a reserved keyword');
      continue;
    }

    variables[key] = value;
  }

  for (final MapEntry(:key, value: variable) in {...variables.entries}) {
    if (variable == null) {
      continue;
    }

    final matches = variablePattern.allMatches('$variable');

    if (matches.isEmpty) {
      continue;
    }

    final keyToCheckForCircular = {key};

    String? resolve(RegExpMatch match, String variable) {
      final referencedVariable = match.group(1);
      final wholeMatch = match.group(0)!;

      if (referencedVariable == null) {
        logger.warn('Variable $referencedVariable is not defined');
        return null;
      }

      if (referencedVariable.startsWith(r'$')) {
        logger.warn(
          'Variable $key is referencing a script, this is forbidden',
        );
        return null;
      }

      if (referencedVariable.startsWith('-')) {
        return variable;
      }

      keyToCheckForCircular.add(referencedVariable);

      final referencedValue = variables[referencedVariable];

      if (referencedValue == null) {
        logger.warn('Variable $referencedVariable is not defined');
        return null;
      }

      var almostResolved = variable.replaceAll(wholeMatch, referencedValue);

      if (variablePattern.hasMatch(almostResolved)) {
        for (final match in variablePattern.allMatches(almostResolved)) {
          // check for circular references
          if (keyToCheckForCircular.contains(match.group(1))) {
            logger.warn('Circular reference detected for variable $key');
            return null;
          }

          final partialResolved = resolve(match, almostResolved);

          if (partialResolved == null) {
            return null;
          }

          almostResolved =
              almostResolved.replaceAll(match.group(0)!, partialResolved);
        }
      }

      return almostResolved;
    }

    for (final match in matches) {
      final resolved = resolve(match, '$variable');

      variables['$key'] = resolved;
    }
  }

  return variables;
}