A library for YAML manipulation with comment and whitespace preservation.

Related tags

Utilities yaml_edit
Overview

Yaml Editor

A library for YAML manipulation while preserving comments.

Usage

A simple usage example:

import 'package:yaml_edit/yaml_edit.dart';

void main() {
  final yamlEditor = YamlEditor('{YAML: YAML}');
  yamlEditor.assign(['YAML'], "YAML Ain't Markup Language");
  print(yamlEditor);
  // Expected output:
  // {YAML: YAML Ain't Markup Language}
}

Testing

Testing is done in two strategies: Unit testing (/test/editor_test.dart) and Golden testing (/test/golden_test.dart). More information on Golden testing and the input/output format can be found at /test/testdata/README.md.

These tests are automatically run with pub run test.

Limitations

  1. Users are not allowed to define tags in the modifications.
  2. Map keys will always be added in the flow style.
Comments
  • How to modify a yaml file with the updated content?

    How to modify a yaml file with the updated content?

    I have the following code that update the yaml content:

    final editor = YamlEditor(mppm.toString());
      editor.appendToList(['packages'], "*");
      print(editor);
    

    Output is:

    {name: mppm, packages: [packages/*, "*"]}
    

    But I am curious how can I add this content back to the yaml file?

    If I use the following code to update the yaml file. It just add it as a normal string without any proper formatting:

    File("pubspec-output.yaml").writeAsStringSync(editor.toString());
    
    question 
    opened by SAGARSURI 1
  • Bump actions/checkout from 3.1.0 to 3.2.0

    Bump actions/checkout from 3.1.0 to 3.2.0

    Bumps actions/checkout from 3.1.0 to 3.2.0.

    Release notes

    Sourced from actions/checkout's releases.

    v3.2.0

    What's Changed

    New Contributors

    Full Changelog: https://github.com/actions/checkout/compare/v3...v3.2.0

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
  • Adding a new key/value to a Map with a non-scalar value leaves trailing whitespace

    Adding a new key/value to a Map with a non-scalar value leaves trailing whitespace

    See this example:

    import 'package:yaml_edit/yaml_edit.dart';
    
    void main() {
      final yamlEditor = YamlEditor('''
    analyzer:
      a:
        b: true
    ''');
      yamlEditor.update(['analyzer', 'language'], {'strict-casts': true});
      var lines = yamlEditor.toString().split('\n');
      print(lines.map((l) => "'$l'").join('\n'));
    }
    

    This results in this output, note the trailing space after language::

    'analyzer:'
    '  a:'
    '    b: true'
    '  language: '
    '    strict-casts: true'
    ''
    
    opened by srawlins 0
  • there is no assign method

    there is no assign method

    in the readme.md

    import 'package:yaml_edit/yaml_edit.dart';
    
    void main() {
      final yamlEditor = YamlEditor('{YAML: YAML}');
      yamlEditor.assign(['YAML'], "YAML Ain't Markup Language");
      print(yamlEditor);
      // Expected output:
      // {YAML: YAML Ain't Markup Language}
    }
    

    https://github.com/dart-lang/yaml_edit/blob/0ae689745e5796e12d77f93f7c2b2bfff7310489/README.md?plain=1#L14 but YamlEditor does not contains this method

    opened by maxzod 0
  • Proposed auxiliary method wrapAsCustomStyledYamlNode

    Proposed auxiliary method wrapAsCustomStyledYamlNode

    This probably needs some test cases, and I'm sure the code can be improved further!

    void main() {
      final doc = YamlEditor('');
      doc.update(
          [],
          wrapAsCustomStyledYamlNode({
            'title': 'Short string as title',
            'description': [
              'Multiple lines with losts of text',
              'that you really makes you want',
              'the YAML to be written with literal strings',
            ].join('\n'),
          }));
      print(doc.toString());
    }
    
    YamlNode wrapAsCustomStyledYamlNode(
      Object? value, {
      CollectionStyle Function(Map map, int depth) styleMap = _defaultMapStyle,
      CollectionStyle Function(List list, int depth) styleList = _defaultListStyle,
      ScalarStyle Function(String string, int depth) styleString =
          _defaultStringStyle,
    }) {
      YamlNode wrap(Object? value, int depth) {
        if (value is YamlScalar) {
          wrapAsYamlNode(value); // assert valid scalar
          return value;
        } else if (value is YamlList) {
          for (final item in value.nodes) {
            wrapAsYamlNode(item); // assert valid scalar
          }
          return value;
        } else if (value is YamlMap) {
          /// Both [entry.key] and [entry.values] are guaranteed to be [YamlNode]s,
          /// so running this will just assert that they are valid scalars.
          for (final entry in value.nodes.entries) {
            wrapAsYamlNode(entry.key);
            wrapAsYamlNode(entry.value);
          }
          return value;
        } else if (value is Map) {
          return wrapAsYamlNode({
            for (final kv in value.entries)
              wrap(kv.key, depth + 1): wrap(kv.value, depth + 1),
          }, collectionStyle: styleMap(value, depth));
        } else if (value is List) {
          return wrapAsYamlNode({
            for (final v in value) wrap(v, depth + 1),
          }, collectionStyle: styleList(value, depth));
        } else if (value is String) {
          return wrapAsYamlNode(value, scalarStyle: styleString(value, depth));
        } else {
          return wrapAsYamlNode(value);
        }
      }
    
      return wrap(value, 0);
    }
    
    int _sizeOfScalar(dynamic value) => value == null ? 4 : '$value'.length;
    
    CollectionStyle _defaultMapStyle(Map map, int depth) {
      if (map.values.any((value) => value is Map || value is List)) {
        return CollectionStyle.BLOCK;
      }
      final size = map.entries.fold<int>(
        0,
        (sum, entry) => sum + _sizeOfScalar(entry.key) + _sizeOfScalar(entry.value),
      );
      if (size < 80) {
        return CollectionStyle.FLOW;
      }
      return CollectionStyle.BLOCK;
    }
    
    CollectionStyle _defaultListStyle(List list, int depth) {
      if (list.any((value) => value is Map || value is List)) {
        return CollectionStyle.BLOCK;
      }
      final size = list.fold<int>(
        0,
        (sum, value) => sum + _sizeOfScalar(value),
      );
      if (size < 80) {
        return CollectionStyle.FLOW;
      }
      return CollectionStyle.BLOCK;
    }
    
    ScalarStyle _defaultStringStyle(String string, int depth) {
      if (string.contains('\n')) {
        return ScalarStyle.LITERAL;
      }
      if (string.length > 80) {
        return ScalarStyle.FOLDED;
      }
      if (!string.contains('\'')) {
        if (!string.contains('"')) {
          return ScalarStyle.PLAIN;
        }
        return ScalarStyle.SINGLE_QUOTED;
      }
      return ScalarStyle.DOUBLE_QUOTED;
    }
    
    enhancement good first issue 
    opened by jonasfj 1
  • `wrapAsYamlNode` should apply style recursively

    `wrapAsYamlNode` should apply style recursively

    https://pub.dev/documentation/yaml_edit/latest/yaml_edit/wrapAsYamlNode.html

    Should this wrap recursively?

    input (lists, strings, maps):

    [{name: bla, packaging: dynamic, path: {path_type: absolute, uri: path/with spaces/bla.dll}, target: windows_x64}]

    output:

    - name: bla
      packaging: dynamic
      path:
        path_type: absolute
        uri: path/with spaces/bla.dll
      target: windows_x64
    

    conversion

    String _toYamlString(Object yamlEncoding) => (YamlEditor('')
          ..update(
              [],
              wrapAsYamlNode(
                yamlEncoding,
                collectionStyle: CollectionStyle.BLOCK,
                scalarStyle: ScalarStyle.DOUBLE_QUOTED,
              )))
        .toString();
    

    Asked for quotes, but didn't get any.

    enhancement 
    opened by dcharkes 3
  • YamlEditor appendToList and insertIntoList inserts new item into next yaml item rather than at end of list

    YamlEditor appendToList and insertIntoList inserts new item into next yaml item rather than at end of list

    The following results in an Unhandled Exception "Assertion failed: (package:yaml_edit) Modification did not result in expected result."

    import 'dart:io';
    
    import 'package:yaml_edit/yaml_edit.dart';
    
    Future<void> main() async {
      YamlEditor yamlEditorFile =
          YamlEditor(File('test/gh_test.yml').readAsStringSync());
    
      try {
        // yamlEditorFile.insertIntoList(
        //     ['school'],
        //     3,
        //     {
        //       'student': {'first_name': 'Henry', 'last_name': 'Smith'}
        //     });
    
        yamlEditorFile.appendToList([
          'school'
        ], {
          'student': {'first_name': 'Henry', 'last_name': 'Smith'}
        });
      } on Exception catch (e) {
        print(e);
      }
    
      print(yamlEditorFile);
    }
    
    

    gh_test.yml is:

    # comment
    school:
      - student:
          first_name: Harry
          last_name: "Reynolds"
      - student:
          first_name: "Sally"
          last_name: Rheims
      - student:
          first_name: "George"
          last_name: "Silver"
    teachers:
      - english:
          name: "Johnson"
      - science:
          name: "Smith"
    

    The insertIntoList call results in the same if the index is the next one (i.e., 3 - commented code in example above).

    Expected behavior in all cases is that it inserts it correctly in the school list after student George Silver.

    Instead the result in both examples is that the inserted list item (Henry Smith) is inserted incorrectly as the first item of the next list (i.e., teachers) as shown by the "Modification did not result in expected result" message.":

    
    > teachers:
    >   - student:
    >       first_name: Henry
    >       last_name: Smith
    >   - english:
    >       name: "Johnson"
    >   - science:
    >       name: "Smith"
    

    the full error message:

    
    Unhandled exception:
    Assertion failed: (package:yaml_edit) Modification did not result in expected result.
    
    # YAML before edit:
    > # comment
    > school:
    >   - student:
    >       first_name: Harry
    >       last_name: "Reynolds"
    >   - student:
    >       first_name: "Sally"
    >       last_name: Rheims
    >   - student:
    >       first_name: "George"
    >       last_name: "Silver"
    > teachers:
    >   - english:
    >       name: "Johnson"
    >   - science:
    >       name: "Smith"
    
    # YAML after edit:
    > # comment
    > school:
    >   - student:
    
    >       first_name: Harry
    >       last_name: "Reynolds"
    >   - student:
    >       first_name: "Sally"
    >       last_name: Rheims
    >   - student:
    >       first_name: "George"
    >       last_name: "Silver"
    > teachers:
    >   - student:
    >       first_name: Henry
    >       last_name: Smith
    >   - english:
    >       name: "Johnson"
    >   - science:
    >       name: "Smith"
    
    Please file an issue at:
    https://github.com/google/dart-neats/issues/new?labels=pkg%3Ayaml_edit%2C+pending-triage&template=yaml_edit.md
    
    #0      YamlEditor._performEdit
    #1      YamlEditor.insertIntoList
    #2      YamlEditor.appendToList
    #3      main
    
    #4      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
    #5      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
    
    Exited (255)
    
    

    So it appears it can't detect the end of the list. The example yaml is correctly validated. If I insert a non-list in between the two lists in the example yaml file, for example the boolean "public: false", I get "Mapping Values are not allowed here" as it attempts to insert it under the boolean yaml:

    Here's that error message:

    
    Error on line 13, column 12: Mapping values are not allowed here. Did you miss a colon earlier?
       ╷
    13 │   - student:
       │            ^
       ╵
    # comment
    school:
      - student:
          first_name: Harry
          last_name: "Reynolds"
      - student:
          first_name: "Sally"
          last_name: Rheims
      - student:
          first_name: "George"
          last_name: "Silver"
    public: false
      - student:
          first_name: Henry
          last_name: Smith
    teachers:
      - english:
          name: "Johnson"
      - science:
          name: "Smith"
    Exited
    
    
    bug 
    opened by GeekVisit 0
  • New API request to replace map key/value pair

    New API request to replace map key/value pair

    I'd love an API (replaceMapEntry?) to replace an entire key/value pair:

    final yamlEditor = YamlEditor('''
    analyzer:
      foo: false
    ''');
      yamlEditor.replaceMapEntry(['analyzer', 'foo'], {'bar': true});
      print(yamlEditor);
    
    /*
    analyzer:
      bar: true
    */
    

    The current methods for doing this don't work well for us:

    • add/update bar, remove foo: results in conflicting SourceEdit offsets.
    • remove foo, add/update bar: results in a flow-style map: analyzer: {foo: false}.

    I'd be happy to contribute this API!

    enhancement 
    opened by srawlins 2
  • String value is escaped in an unwanted way

    String value is escaped in an unwanted way

    When updating a string value with forward slashes, the resulting string escapes the forward slashes unnecessarily:

    final doc = YamlEditor('exampleKey: \${file(../../bar.yml)}');
    
    doc.update(['exampleKey'], '\${file(../../foo.yml)}');
    
    print(doc);
    // exampleKey: ${file(..\/..\/bar.yml)}
    

    My current workaround is to modify the resulting string:

    doc.toString().replaceAll('\\/', '/')
    

    It would be nice to customize that behaviour.

    enhancement 
    opened by Schwusch 2
  • how to set value to if the path path does not exist

    how to set value to if the path path does not exist

    iam working on CLI features that see the content of the assets directory and add pathes to pubspec.yaml based on that it works with update if there is already a list in pubspec assets

        final doc = YamlEditor(pubspecContent);
        doc.update(['flutter','assets'], assets);
    

    BUT if there is no assets key n the pubspec under flutter

    
    flutter:
      uses-material-design: true
    

    it throws exception

    i think this is the expected behavior to throw when updating a null but how could i set value to a key that does not exist+

    opened by maxzod 1
Owner
Dart
Dart is an open-source, scalable programming language, with robust libraries and runtimes, for building web, server, and mobile apps.
Dart
A CLI for syncing Dart dependency versions between pubspec.yaml and pubspec.lock files.

lockpick A CLI for syncing Dart dependency versions between pubspec.yaml and pubspec.lock files. ?? Usage # Activate lockpick pub global activate lock

Jeroen Meijer (Jay) 34 Oct 17, 2022
null 2 Apr 17, 2022
A set of commands for coverage info files manipulation.

Coverage Utils A set of commands for coverage info files manipulation. Installing $ dart pub global activate

Karlo Verde 22 Oct 9, 2022
The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

null 2 Oct 8, 2021
Library for help you make userbot or bot telegram and support tdlib telegram database and only support nodejs dart and google-apps-script

To-Do telegram client dart ✅️ support multi token ( bot / userbot ) ✅️ support bot and userbot ✅️ support telegram-bot-api local server ✅️ support tel

Azka Full Snack Developer:) 73 Jan 7, 2023
Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Luke Pighetti 3.5k Jan 4, 2023
Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more!

Scribble Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more! A

Tim Created It. 73 Dec 16, 2022
A JMAP client library in Dart to make JMAP method calls and process the responses

JMAP Dart client A JMAP client library to make JMAP method calls and process the responses. We most notably use it to write the TMail Flutter applicat

LINAGORA 18 Dec 19, 2022
Flutter library to add gestures and animations to each Shape you draw on your canvas in your CustomPainter

Flutter library to bring your CustomPainter ?? to Life ✨ ⚡️ touchable library gives you the ability to add various gestures and animations to each Sha

Natesh Bhat 190 Jan 7, 2023
A library to process noun, verb and adjectives transformations/conjugation.

A library to process noun (plural to singular and singular to plural), verb (gerund, present & past) and adjectives (comparative & superlative) transformations/conjugation.

Kawaljeet Singh 6 Nov 4, 2022
A Dart library to parse Portable Executable (PE) format

pefile A Dart library to parse Portable Executable (PE) format Usage A simple usage example: var pe = pefile.parse('C:\\Windows\\System32\\notepad.exe

null 4 Sep 12, 2022
This library contains methods that make it easy to consume Mpesa Api.

This library contains methods that make it easy to consume Mpesa Api. It's multi-platform, and supports CLI, server, mobile, desktop, and the browser.

Eddie Genius 3 Dec 15, 2021
An alternative random library for Dart.

Randt Randt library for Dart... Description Use Randt to get a random integer from a list, generate random integer in a specific range and generate ra

Bangladesh Coding Soldierz 3 Nov 21, 2021
Boilerplate-free form validation library

Valform Boilerplate-free form validation library. Preface Why? Why not Formz? Why Valform? Getting started Simple Usage Inspiration Why? There is no c

Alexander Farkas 3 Nov 14, 2021
Boilerplate-free form validation library

Trigger Boilerplate-free form validation library. Preface Why? Why not Formz? Why Trigger? Getting started Simple Usage Inspiration Why? There is no c

Alexander Farkas 3 Nov 14, 2021
A fast algorithm for finding polygon pole of inaccessibility implemented as a Dart library.

polylabel Dart port of https://github.com/mapbox/polylabel. A fast algorithm for finding polygon pole of inaccessibility implemented as a Dart library

André Sousa 2 Nov 13, 2021
Redux Compact is a library that aims to reduce the massive amount of boilerplate that follows maintaining a Flutter Redux application.

Redux Compact Redux Compact is a library that aims to reduce the massive amount of boilerplate that follows maintaining a Flutter Redux application. T

Ómar Óskarsson 9 Apr 8, 2022
Dart library for unescaping HTML-encoded strings

html_unescape A Dart library for unescaping HTML-encoded strings. Supports: Named Character References ( ) 2099 of them Decimal Character Referen

Filip Hracek 36 Dec 20, 2022
The `TypedEventNotifier` library allows notifying listeners with an object.

The TypedEventNotifier library allows notifying listeners with an object. listeners can be subscribed to only a special type or group of objects.

Evgeniy Ilyin 0 Nov 13, 2021