| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:analyzer/analysis_rule/pubspec.dart'; | |
| import 'package:analyzer/file_system/file_system.dart'; | |
| import 'package:analyzer/source/file_source.dart'; | |
| import 'package:analyzer/source/source.dart'; | |
| import 'package:source_span/source_span.dart'; | |
| import 'package:yaml/yaml.dart'; | |
| PubspecEntry? _findEntry( | |
| YamlMap map, | |
| String key, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| PubspecEntry? entry; | |
| map.nodes.forEach((k, v) { | |
| if (k is YamlScalar && key == k.toString()) { | |
| entry = _processScalar(k, v, resourceProvider); | |
| } | |
| }); | |
| return entry; | |
| } | |
| PubspecDependencyList? _processDependencies( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is! YamlMap) { | |
| return null; | |
| } | |
| _PubspecDependencyList deps = _PubspecDependencyList( | |
| PubspecNodeImpl(key, resourceProvider), | |
| ); | |
| value.nodes.forEach((k, v) { | |
| if (k is YamlScalar) deps.add(_PubspecDependency(k, v, resourceProvider)); | |
| }); | |
| return deps; | |
| } | |
| PubspecEnvironment? _processEnvironment( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is! YamlMap) { | |
| return null; | |
| } | |
| return _PubspecEnvironment( | |
| PubspecNodeImpl(key, resourceProvider), | |
| flutter: _findEntry(value, 'flutter', resourceProvider), | |
| sdk: _findEntry(value, 'sdk', resourceProvider), | |
| ); | |
| } | |
| PubspecGitRepo? _processGitRepo( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is YamlScalar) { | |
| var token = PubspecNodeImpl(key, resourceProvider); | |
| return _PubspecGitRepo( | |
| token, | |
| url: PubspecEntry(token, PubspecNodeImpl(value, resourceProvider)), | |
| ); | |
| } | |
| if (value is! YamlMap) { | |
| return null; | |
| } | |
| // url: git://github.com/munificent/kittens.git | |
| // ref: some-branch | |
| return _PubspecGitRepo( | |
| PubspecNodeImpl(key, resourceProvider), | |
| ref: _findEntry(value, 'ref', resourceProvider), | |
| url: _findEntry(value, 'url', resourceProvider), | |
| ); | |
| } | |
| PubspecHost? _processHost( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is YamlScalar) { | |
| // dependencies: | |
| // mypkg: | |
| // hosted: https://some-pub-server.com | |
| // version: ^1.2.3 | |
| return _PubspecHost( | |
| PubspecNodeImpl(key, resourceProvider), | |
| isShortForm: true, | |
| url: _processScalar(key, value, resourceProvider), | |
| ); | |
| } | |
| if (value is YamlMap) { | |
| // name: transmogrify | |
| // url: http://your-package-server.com | |
| return _PubspecHost( | |
| PubspecNodeImpl(key, resourceProvider), | |
| isShortForm: false, | |
| name: _findEntry(value, 'name', resourceProvider), | |
| url: _findEntry(value, 'url', resourceProvider), | |
| ); | |
| } | |
| return null; | |
| } | |
| PubspecEntry? _processScalar( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is! YamlScalar) { | |
| return null; | |
| //WARN? | |
| } | |
| return PubspecEntry( | |
| PubspecNodeImpl(key, resourceProvider), | |
| PubspecNodeImpl(value, resourceProvider), | |
| ); | |
| } | |
| PubspecNodeList? _processScalarList( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| if (value is! YamlList) { | |
| return null; | |
| } | |
| return _PubspecNodeList( | |
| PubspecNodeImpl(key, resourceProvider), | |
| value.nodes.whereType<YamlScalar>().map( | |
| (n) => PubspecNodeImpl(n, resourceProvider), | |
| ), | |
| ); | |
| } | |
| abstract class Pubspec { | |
| factory Pubspec.parse( | |
| String pubspec, { | |
| Uri? sourceUrl, | |
| required ResourceProvider resourceProvider, | |
| }) { | |
| try { | |
| var yaml = loadYamlNode(pubspec, sourceUrl: sourceUrl); | |
| return Pubspec.parseYaml(yaml, resourceProvider: resourceProvider); | |
| } on Exception { | |
| return _Pubspec.parse(YamlMap(), resourceProvider: resourceProvider); | |
| } | |
| } | |
| factory Pubspec.parseYaml( | |
| YamlNode yaml, { | |
| required ResourceProvider resourceProvider, | |
| }) { | |
| return _Pubspec.parse(yaml, resourceProvider: resourceProvider); | |
| } | |
| PubspecEntry? get author; | |
| PubspecNodeList? get authors; | |
| PubspecDependencyList? get dependencies; | |
| PubspecDependencyList? get dependencyOverrides; | |
| PubspecEntry? get description; | |
| PubspecDependencyList? get devDependencies; | |
| PubspecEntry? get documentation; | |
| PubspecEnvironment? get environment; | |
| PubspecEntry? get homepage; | |
| PubspecEntry? get issueTracker; | |
| PubspecEntry? get name; | |
| PubspecEntry? get repository; | |
| PubspecEntry? get resolution; | |
| PubspecEntry? get version; | |
| PubspecNodeList? get workspace; | |
| void accept(PubspecVisitor visitor); | |
| } | |
| class PubspecNodeImpl implements PubspecNode { | |
| @override | |
| final String? text; | |
| @override | |
| final SourceSpan span; | |
| final ResourceProvider _resourceProvider; | |
| PubspecNodeImpl(YamlScalar node, this._resourceProvider) | |
| : text = node.value?.toString(), | |
| span = node.span; | |
| /// The [Source] information of the pubspec file in which this node is located. | |
| Source get source { | |
| var uri = span.sourceUrl!; | |
| var filePath = _resourceProvider.pathContext.fromUri(uri); | |
| var file = _resourceProvider.getFile(filePath); | |
| return FileSource(file, uri); | |
| } | |
| @override | |
| String toString() => '$text'; | |
| } | |
| class _Pubspec implements Pubspec { | |
| @override | |
| final PubspecEntry? author; | |
| @override | |
| final PubspecNodeList? authors; | |
| @override | |
| final PubspecNodeList? workspace; | |
| @override | |
| final PubspecEntry? description; | |
| @override | |
| final PubspecEntry? documentation; | |
| @override | |
| final PubspecEnvironment? environment; | |
| @override | |
| final PubspecEntry? homepage; | |
| @override | |
| final PubspecEntry? issueTracker; | |
| @override | |
| final PubspecEntry? name; | |
| @override | |
| final PubspecEntry? repository; | |
| @override | |
| final PubspecEntry? resolution; | |
| @override | |
| final PubspecEntry? version; | |
| @override | |
| final PubspecDependencyList? dependencies; | |
| @override | |
| final PubspecDependencyList? devDependencies; | |
| @override | |
| final PubspecDependencyList? dependencyOverrides; | |
| factory _Pubspec.parse( | |
| YamlNode yaml, { | |
| required ResourceProvider resourceProvider, | |
| }) { | |
| if (yaml is! YamlMap) { | |
| return _Pubspec._(); | |
| } | |
| PubspecEntry? author; | |
| PubspecNodeList? authors; | |
| PubspecNodeList? workspace; | |
| PubspecEntry? description; | |
| PubspecEntry? documentation; | |
| PubspecEnvironment? environment; | |
| PubspecEntry? homepage; | |
| PubspecEntry? issueTracker; | |
| PubspecEntry? name; | |
| PubspecEntry? repository; | |
| PubspecEntry? resolution; | |
| PubspecEntry? version; | |
| PubspecDependencyList? dependencies; | |
| PubspecDependencyList? devDependencies; | |
| PubspecDependencyList? dependencyOverrides; | |
| yaml.nodes.forEach((key, v) { | |
| if (key is! YamlScalar) { | |
| return; | |
| } | |
| switch (key.toString()) { | |
| case 'author': | |
| author = _processScalar(key, v, resourceProvider); | |
| case 'authors': | |
| authors = _processScalarList(key, v, resourceProvider); | |
| case 'homepage': | |
| homepage = _processScalar(key, v, resourceProvider); | |
| case 'repository': | |
| repository = _processScalar(key, v, resourceProvider); | |
| case 'issue_tracker': | |
| issueTracker = _processScalar(key, v, resourceProvider); | |
| case 'name': | |
| name = _processScalar(key, v, resourceProvider); | |
| case 'description': | |
| description = _processScalar(key, v, resourceProvider); | |
| case 'documentation': | |
| documentation = _processScalar(key, v, resourceProvider); | |
| case 'dependencies': | |
| dependencies = _processDependencies(key, v, resourceProvider); | |
| case 'dev_dependencies': | |
| devDependencies = _processDependencies(key, v, resourceProvider); | |
| case 'dependency_overrides': | |
| dependencyOverrides = _processDependencies(key, v, resourceProvider); | |
| case 'environment': | |
| environment = _processEnvironment(key, v, resourceProvider); | |
| case 'version': | |
| version = _processScalar(key, v, resourceProvider); | |
| case 'resolution': | |
| resolution = _processScalar(key, v, resourceProvider); | |
| case 'workspace': | |
| workspace = _processScalarList(key, v, resourceProvider); | |
| } | |
| }); | |
| return _Pubspec._( | |
| author: author, | |
| authors: authors, | |
| description: description, | |
| documentation: documentation, | |
| environment: environment, | |
| homepage: homepage, | |
| issueTracker: issueTracker, | |
| name: name, | |
| repository: repository, | |
| version: version, | |
| dependencies: dependencies, | |
| devDependencies: devDependencies, | |
| dependencyOverrides: dependencyOverrides, | |
| resolution: resolution, | |
| workspace: workspace, | |
| ); | |
| } | |
| _Pubspec._({ | |
| this.author, | |
| this.authors, | |
| this.workspace, | |
| this.description, | |
| this.documentation, | |
| this.environment, | |
| this.homepage, | |
| this.issueTracker, | |
| this.name, | |
| this.repository, | |
| this.version, | |
| this.dependencies, | |
| this.devDependencies, | |
| this.dependencyOverrides, | |
| this.resolution, | |
| }); | |
| @override | |
| void accept(PubspecVisitor visitor) { | |
| if (author case var author?) { | |
| visitor.visitPackageAuthor(author); | |
| } | |
| if (authors case var authors?) { | |
| visitor.visitPackageAuthors(authors); | |
| } | |
| if (description case var description?) { | |
| visitor.visitPackageDescription(description); | |
| } | |
| if (documentation case var documentation?) { | |
| visitor.visitPackageDocumentation(documentation); | |
| } | |
| if (environment case var environment?) { | |
| visitor.visitPackageEnvironment(environment); | |
| } | |
| if (homepage case var homepage?) { | |
| visitor.visitPackageHomepage(homepage); | |
| } | |
| if (issueTracker case var issueTracker?) { | |
| visitor.visitPackageIssueTracker(issueTracker); | |
| } | |
| if (repository case var repository?) { | |
| visitor.visitPackageRepository(repository); | |
| } | |
| if (name case var name?) { | |
| visitor.visitPackageName(name); | |
| } | |
| if (version case var version?) { | |
| visitor.visitPackageVersion(version); | |
| } | |
| if (dependencies case var dependencies?) { | |
| visitor.visitPackageDependencies(dependencies); | |
| dependencies.forEach(visitor.visitPackageDependency); | |
| } | |
| if (devDependencies case var devDependencies?) { | |
| visitor.visitPackageDevDependencies(devDependencies); | |
| devDependencies.forEach(visitor.visitPackageDevDependency); | |
| } | |
| if (dependencyOverrides case var dependencyOverrides?) { | |
| visitor.visitPackageDependencyOverrides(dependencyOverrides); | |
| dependencyOverrides.forEach(visitor.visitPackageDependencyOverride); | |
| } | |
| } | |
| @override | |
| String toString() { | |
| var sb = StringBuffer(); | |
| sb.maybeWrite(name); | |
| sb.maybeWrite(version); | |
| sb.maybeWrite(author); | |
| sb.maybeWrite(authors); | |
| sb.maybeWrite(description); | |
| sb.maybeWrite(homepage); | |
| sb.maybeWrite(repository); | |
| sb.maybeWrite(issueTracker); | |
| sb.maybeWrite(dependencies); | |
| sb.maybeWrite(devDependencies); | |
| sb.maybeWrite(dependencyOverrides); | |
| return sb.toString(); | |
| } | |
| } | |
| class _PubspecDependency extends PubspecDependency { | |
| @override | |
| final PubspecNode? name; | |
| @override | |
| final PubspecEntry? path; | |
| @override | |
| final PubspecEntry? version; | |
| @override | |
| final PubspecHost? host; | |
| @override | |
| final PubspecGitRepo? git; | |
| factory _PubspecDependency( | |
| YamlScalar key, | |
| YamlNode value, | |
| ResourceProvider resourceProvider, | |
| ) { | |
| var name = PubspecNodeImpl(key, resourceProvider); | |
| PubspecEntry? path; | |
| PubspecEntry? version; | |
| PubspecHost? host; | |
| PubspecGitRepo? git; | |
| if (value is YamlScalar) { | |
| // Simple version constraint. | |
| version = PubspecEntry(null, PubspecNodeImpl(value, resourceProvider)); | |
| } else if (value is YamlMap) { | |
| value.nodes.forEach((key, v) { | |
| if (key is! YamlScalar) { | |
| return; | |
| } | |
| switch (key.toString()) { | |
| case 'path': | |
| path = _processScalar(key, v, resourceProvider); | |
| case 'version': | |
| version = _processScalar(key, v, resourceProvider); | |
| case 'hosted': | |
| host = _processHost(key, v, resourceProvider); | |
| case 'git': | |
| git = _processGitRepo(key, v, resourceProvider); | |
| } | |
| }); | |
| } | |
| return _PubspecDependency._( | |
| name: name, | |
| path: path, | |
| version: version, | |
| host: host, | |
| git: git, | |
| ); | |
| } | |
| _PubspecDependency._({ | |
| required this.name, | |
| required this.path, | |
| required this.version, | |
| required this.host, | |
| required this.git, | |
| }); | |
| @override | |
| String toString() { | |
| var sb = StringBuffer(); | |
| if (name != null) { | |
| sb.write('$name:'); | |
| } | |
| var versionInfo = ''; | |
| if (version != null) { | |
| if (version!.key == null) { | |
| versionInfo = ' $version'; | |
| } else { | |
| versionInfo = '\n $version'; | |
| } | |
| } | |
| sb.writeln(versionInfo); | |
| if (host != null) { | |
| sb.writeln(host); | |
| } | |
| if (git != null) { | |
| sb.writeln(git); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| class _PubspecDependencyList extends PubspecDependencyList { | |
| final dependencies = <PubspecDependency>[]; | |
| final PubspecNode token; | |
| _PubspecDependencyList(this.token); | |
| @override | |
| Iterator<PubspecDependency> get iterator => dependencies.iterator; | |
| void add(PubspecDependency? dependency) { | |
| if (dependency != null) { | |
| dependencies.add(dependency); | |
| } | |
| } | |
| @override | |
| String toString() => '$token\n${dependencies.join(' ')}'; | |
| } | |
| class _PubspecEnvironment implements PubspecEnvironment { | |
| @override | |
| final PubspecNode token; | |
| @override | |
| final PubspecEntry? flutter; | |
| @override | |
| final PubspecEntry? sdk; | |
| _PubspecEnvironment(this.token, {required this.flutter, required this.sdk}); | |
| @override | |
| String toString() => | |
| ''' | |
| $token: | |
| $sdk | |
| $flutter'''; | |
| } | |
| class _PubspecGitRepo implements PubspecGitRepo { | |
| @override | |
| final PubspecNode token; | |
| @override | |
| final PubspecEntry? ref; | |
| @override | |
| final PubspecEntry? url; | |
| _PubspecGitRepo(this.token, {this.ref, required this.url}); | |
| @override | |
| String toString() => | |
| ''' | |
| $token: | |
| $url | |
| $ref'''; | |
| } | |
| class _PubspecHost implements PubspecHost { | |
| @override | |
| final bool isShortForm; | |
| @override | |
| final PubspecEntry? name; | |
| @override | |
| final PubspecNode token; | |
| @override | |
| final PubspecEntry? url; | |
| _PubspecHost(this.token, {required this.isShortForm, this.name, this.url}); | |
| @override | |
| String toString() => | |
| ''' | |
| $token: | |
| $name | |
| $url'''; | |
| } | |
| class _PubspecNodeList extends PubspecNodeList { | |
| @override | |
| final PubspecNode token; | |
| final Iterable<PubspecNode> nodes; | |
| _PubspecNodeList(this.token, this.nodes); | |
| @override | |
| Iterator<PubspecNode> get iterator => nodes.iterator; | |
| @override | |
| String toString() => | |
| ''' | |
| $token: | |
| - ${nodes.join('\n - ')}'''; | |
| } | |
| extension on StringBuffer { | |
| void maybeWrite(Object? value) { | |
| if (value != null) { | |
| writeln(value); | |
| } | |
| } | |
| } |