tools icon indicating copy to clipboard operation
tools copied to clipboard

Can toJson() functionality be included as well?

Open thosakwe opened this issue 7 years ago • 8 comments

It's fantastic to have an official package on Pub for parsing and extracting data from a pubspec.

There is, however, one thing, I'm wondering - would it be bad for createToJson to be true for the models contained in this package?

I'm working on updating the Angel CLI, which uses the old package:pubspec to persist changes to the project files (namely, adding dependencies the user is missing), to pubspec_parse, and if there were some way to just get a toJson method on the PubSpec.

I'll gladly send a PR, but is there is a specific reason this functionality isn't present already? I imagine there's probably a reason I didn't think about.

thosakwe avatar Jul 14 '18 21:07 thosakwe

Hey @thosakwe

I think I avoided toJson because the pubspec is yaml – and the json output wouldn't necessarily round-trip the shape of the source.

There is quite a bit of special handling of dependencies, for instance.

If you want to take a shot, I'd accept a PR, though!

kevmoo avatar Jul 21 '18 04:07 kevmoo

@kevmoo could I work on this issue?

AliAkberAakash avatar Feb 05 '23 16:02 AliAkberAakash

@kevmoo could I work on this issue?

Sure! Just make sure you put in plenty of tests. I want to see that at round trips cleanly, etc.

kevmoo avatar Feb 05 '23 19:02 kevmoo

Is this being worked on? (cc: @AliAkberAakash , @kevmoo )

alestiago avatar Feb 16 '24 10:02 alestiago

Hey @alestiago I completely forgot about this, No it's not being worked on from my side. If you want we can work on this together.

AliAkberAakash avatar Feb 16 '24 11:02 AliAkberAakash

@AliAkberAakash I don't have the bandwidth right now to work on this feature. If I ever have some spare I'll let you know. If you're no longer working on this I recommend asking to get unassigned.

alestiago avatar Jul 09 '24 09:07 alestiago

I think I avoided toJson because the pubspec is yaml – and the json output wouldn't necessarily round-trip the shape of the source.

So I know in general that YAML is a superset of JSON, but Is there any sort of valid Pubspec that can't be represented by JSON, though? I threw this together pretty quickly -- is something like this a good starting point for a PR?

Pubspec.toJson()

import "package:pubspec_parse/pubspec_parse.dart";
import "package:pub_semver/pub_semver.dart";

extension <K, V> on Map<K, V> {
  Iterable<(K, V)> get records sync* {
    for (final entry in entries) {
      yield (entry.key, entry.value);
    }
  }
}

typedef Json = Map<String, dynamic>;

extension on Dependency {
  Json toJson() => switch (this) {
    SdkDependency(:final sdk, :final version) => {
      "sdk": sdk,
      "version": version.toString(),
    },
    HostedDependency(:final hosted, :final version) => {
      if (hosted != null) "hosted": hosted.url.toString(),
      "version": version.toString(),
    },
    GitDependency(:final url, :final ref, :final path) => {
      "git": {
        "url": url.toString(),
        if (path != null) "ref": ref,
        if (path != null) "path": path,
      },
    },
    PathDependency(:final path) => {
      "path": path.replaceAll(r'\', '/'),
    },
  };
}

extension on Pubspec {
  Json toJson() => {
    "name": name,
    "environment": {
      for (final (sdk, version) in environment.records)
        sdk: version.toString(),
    },
    if (resolution != null) "resolution": resolution,
    if (workspace != null) "workspace": workspace,
    "dependencies": {
      for (final (name, dependency) in dependencies.records)
        name: dependency.toJson(),
    },
    // ...
  };
}

That converts this

final pubspec = Pubspec("workspace",
  environment: {"sdk": Version.parse("3.6.0")},
  dependencies: {
    "http": HostedDependency(
      version: VersionRange(min: Version.parse("1.2.0"), max: Version.parse("2.0.0")),
      hosted: HostedDetails(null, Uri.parse('https://pub.dev')),
    ),
    "pub_cache_server": PathDependency(r"D:\dart\packages\pub_cache_server"),
    "flutter": SdkDependency("flutter"),
    "pubspec_parse": GitDependency(
      Uri.parse('https://github.com/Levi-Lesches/dart-lang-tools'),
      ref: 'pubspec-workspaces-1814',
      path: 'pkgs/pubspec_parse',
    ),
  },
);

into this

{
  "name": "workspace",
  "environment": {
    "sdk": "3.6.0"
  },
  "dependencies": {
    "http": {
      "hosted": "https://pub.dev",
      "version": ">1.2.0 <2.0.0"
    },
    "pub_cache_server": {
      "path": "D:/dart/packages/pub_cache_server"
    },
    "flutter": {
      "sdk": "flutter",
      "version": "any"
    },
    "pubspec_parse": {
      "git": {
        "url": "https://github.com/Levi-Lesches/dart-lang-tools",
        "ref": "pubspec-workspaces-1814",
        "path": "pkgs/pubspec_parse"
      }
    }
  }
}

Then, saving that to a file pubspec.yaml and running dart pub get in that directory works just fine:

dart pub get output

Resolving dependencies...
Downloading packages...
+ args 2.6.0
+ async 2.12.0
+ characters 1.3.0 (1.4.0 available)
+ checked_yaml 2.0.3
+ collection 1.19.0 (1.19.1 available)
+ flutter 0.0.0 from sdk flutter
+ http 1.2.2
+ http_methods 1.1.1
+ http_parser 4.1.2
+ json_annotation 4.9.0
+ material_color_utilities 0.11.1 (0.12.0 available)
+ meta 1.15.0 (1.16.0 available)
+ path 1.9.1
+ pub_cache_server 1.0.1 from path D:\dart\packages\pub_cache_server
+ pub_semver 2.1.5
+ pubspec_parse 1.5.0-wip from git https://github.com/Levi-Lesches/dart-lang-tools at d7f79e in pkgs/pubspec_parse
+ shelf 1.4.2
+ shelf_router 1.1.4
+ sky_engine 0.0.0 from sdk flutter
+ source_span 1.10.1
+ stack_trace 1.12.1
+ stream_channel 2.1.3
+ string_scanner 1.4.1
+ tar 2.0.0
+ term_glyph 1.2.2
+ typed_data 1.4.0
+ vector_math 2.1.4
+ web 1.1.0
+ yaml 3.1.3
Changed 29 dependencies!
4 packages have newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.

It may not look like an "idiomatic" Pubspec, but in the meantime, a .toJson() would be valuable for automatically constructing and testing Pubspecs. My use case is that I'm trying to expand the test suite for package:dependency_validator and I would like to construct Pubspec objects and write them to files instead of hand-crafting multi-line strings. In terms of tests, I'm thinking just generating a bunch of Pubspec() objects and testing that Pubspec.fromJson(pubspec.toJson()) == pubspec) (which would also mean adding some notion of equality as well)

Levi-Lesches avatar Jan 03 '25 09:01 Levi-Lesches

Adding the above toJson() method really helped clean up my tests. See workspace_test.dart and the checkWorkspace() for an example.

Levi-Lesches avatar Jan 08 '25 01:01 Levi-Lesches