Skip to content

Commit 39184b4

Browse files
committed
Platform detection basics.
1 parent 3c37903 commit 39184b4

File tree

4 files changed

+363
-0
lines changed

4 files changed

+363
-0
lines changed

lib/src/platform.dart

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import 'pubspec.dart';
2+
3+
abstract class KnownPlatforms {
4+
static const String browser = 'browser';
5+
static const String console = 'console';
6+
static const String flutter = 'flutter';
7+
static const String mirrors = 'mirrors';
8+
9+
static const String angular = 'angular';
10+
}
11+
12+
class PlatformSummary {
13+
final Platform package;
14+
final Map<String, Platform> libraries;
15+
16+
PlatformSummary(this.package, this.libraries);
17+
18+
bool get hasConflict =>
19+
package.hasConflict ||
20+
libraries.values.any((p) => p.hasConflict) ||
21+
_conflictsFlutter;
22+
23+
bool get _conflictsFlutter =>
24+
package.uses.contains(KnownPlatforms.flutter) &&
25+
libraries.values.any((p) => !p.worksInFlutter);
26+
}
27+
28+
class Platform {
29+
final List<String> uses;
30+
31+
Platform._(this.uses);
32+
33+
factory Platform(Iterable<String> uses) =>
34+
new Platform._((uses ?? []).toList()..sort());
35+
36+
factory Platform.fromMap(Map<String, dynamic> map) =>
37+
new Platform(map['uses']);
38+
39+
bool get hasConflict =>
40+
(!worksAnywhere) ||
41+
(uses.contains(KnownPlatforms.flutter) && !worksInFlutter);
42+
43+
bool get worksEverywhere =>
44+
worksInBrowser && worksInConsole && worksInFlutter;
45+
46+
bool get worksAnywhere => worksInBrowser || worksInConsole || worksInFlutter;
47+
48+
bool get worksInBrowser =>
49+
_hasNoUseOf([KnownPlatforms.flutter]) &&
50+
(uses.contains(KnownPlatforms.browser) ||
51+
_hasNoUseOf([KnownPlatforms.console]));
52+
53+
bool get worksInConsole =>
54+
_hasNoUseOf([KnownPlatforms.browser, KnownPlatforms.flutter]);
55+
56+
bool get worksInFlutter =>
57+
_hasNoUseOf([KnownPlatforms.browser, KnownPlatforms.mirrors]);
58+
59+
bool _hasNoUseOf(Iterable<String> platforms) =>
60+
!platforms.any((p) => uses.contains(p));
61+
}
62+
63+
PlatformSummary classifyPlatforms(
64+
Pubspec pubspec, Map<String, List<String>> transientLibs) {
65+
Set<String> uses = new Set();
66+
67+
bool isFlutterOnly = pubspec.hasFlutterKey || pubspec.dependsOnFlutterSdk;
68+
if (isFlutterOnly) {
69+
uses.add(KnownPlatforms.flutter);
70+
}
71+
72+
return new PlatformSummary(
73+
new Platform(uses),
74+
new Map.fromIterable(transientLibs.keys,
75+
value: (key) => classifyPlatform(transientLibs[key])));
76+
}
77+
78+
Platform classifyPlatform(Iterable<String> dependencies) {
79+
Set<String> libs = dependencies.toSet();
80+
Set<String> uses = new Set();
81+
82+
if (_webPackages.any(libs.contains)) {
83+
uses.add(KnownPlatforms.browser);
84+
}
85+
86+
if (libs.contains('dart:io')) {
87+
uses.add(KnownPlatforms.console);
88+
}
89+
90+
if (libs.contains('dart:ui')) {
91+
uses.add(KnownPlatforms.flutter);
92+
}
93+
94+
if (libs.contains('dart:mirrors')) {
95+
uses.add(KnownPlatforms.mirrors);
96+
}
97+
98+
// packages
99+
if (libs.any((p) => p.startsWith('package:angular2/'))) {
100+
uses.add(KnownPlatforms.angular);
101+
}
102+
103+
return new Platform(uses);
104+
}
105+
106+
const List<String> _webPackages = const [
107+
'dart:html',
108+
'dart:indexed_db',
109+
'dart:js',
110+
'dart:js_util',
111+
'dart:svg',
112+
'dart:web_audio',
113+
'dart:web_gl',
114+
'dart:web_sql',
115+
];

lib/src/pubspec.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class Pubspec {
2+
final Map<String, dynamic> _data;
3+
4+
Pubspec(this._data);
5+
6+
Map<String, dynamic> get dependencies => _data['dependencies'];
7+
8+
bool get hasFlutterKey => _data.containsKey('flutter');
9+
bool get hasFlutterPluginKey =>
10+
hasFlutterKey &&
11+
_data['flutter'] is Map &&
12+
_data['flutter']['plugin'] != null;
13+
14+
bool get dependsOnFlutterSdk {
15+
if (dependencies == null) return false;
16+
return dependencies.values
17+
.any((value) => value is Map && value['sdk'] == 'flutter');
18+
}
19+
20+
bool get dependsOnAnyVersion {
21+
if (dependencies == null) return false;
22+
return dependencies.values.any((v) {
23+
if (v is String) return v == 'any';
24+
if (v is Map) return v['version'] == 'any';
25+
return false;
26+
});
27+
}
28+
}

test/platform_test.dart

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import 'package:pana/src/platform.dart';
2+
import 'package:test/test.dart';
3+
4+
import 'pubspec_test.dart';
5+
6+
void main() {
7+
group('Platform', () {
8+
test('no libraries', () {
9+
Platform p = classifyPlatform([]);
10+
expect(p.hasConflict, isFalse);
11+
expect(p.worksAnywhere, isTrue);
12+
expect(p.worksEverywhere, isTrue);
13+
expect(p.worksInBrowser, isTrue);
14+
expect(p.worksInConsole, isTrue);
15+
expect(p.worksInFlutter, isTrue);
16+
});
17+
18+
test('unknown library', () {
19+
Platform p = classifyPlatform(['package:_unknown/_unknown.dart']);
20+
expect(p.hasConflict, isFalse);
21+
expect(p.worksAnywhere, isTrue);
22+
expect(p.worksEverywhere, isTrue);
23+
expect(p.worksInBrowser, isTrue);
24+
expect(p.worksInConsole, isTrue);
25+
expect(p.worksInFlutter, isTrue);
26+
});
27+
28+
test('dart:io', () {
29+
Platform p = classifyPlatform(['dart:io']);
30+
expect(p.hasConflict, isFalse);
31+
expect(p.worksAnywhere, isTrue);
32+
expect(p.worksEverywhere, isFalse);
33+
expect(p.worksInBrowser, isFalse);
34+
expect(p.worksInConsole, isTrue);
35+
expect(p.worksInFlutter, isTrue);
36+
});
37+
38+
test('dart:html', () {
39+
Platform p = classifyPlatform(['dart:html']);
40+
expect(p.hasConflict, isFalse);
41+
expect(p.worksAnywhere, isTrue);
42+
expect(p.worksEverywhere, isFalse);
43+
expect(p.worksInBrowser, isTrue);
44+
expect(p.worksInConsole, isFalse);
45+
expect(p.worksInFlutter, isFalse);
46+
47+
p = classifyPlatform(['dart:svg']);
48+
expect(p.hasConflict, isFalse);
49+
expect(p.worksAnywhere, isTrue);
50+
expect(p.worksEverywhere, isFalse);
51+
expect(p.worksInBrowser, isTrue);
52+
expect(p.worksInConsole, isFalse);
53+
expect(p.worksInFlutter, isFalse);
54+
});
55+
56+
test('dart:ui', () {
57+
Platform p = classifyPlatform(['dart:ui']);
58+
expect(p.hasConflict, isFalse);
59+
expect(p.worksAnywhere, isTrue);
60+
expect(p.worksEverywhere, isFalse);
61+
expect(p.worksInBrowser, isFalse);
62+
expect(p.worksInConsole, isFalse);
63+
expect(p.worksInFlutter, isTrue);
64+
});
65+
66+
test('dart:mirrors', () {
67+
Platform p = classifyPlatform(['dart:mirrors']);
68+
expect(p.hasConflict, isFalse);
69+
expect(p.worksAnywhere, isTrue);
70+
expect(p.worksEverywhere, isFalse);
71+
expect(p.worksInBrowser, isTrue);
72+
expect(p.worksInConsole, isTrue);
73+
expect(p.worksInFlutter, isFalse);
74+
});
75+
76+
test('http package: both html and io', () {
77+
Platform p = classifyPlatform(['dart:html', 'dart:io']);
78+
expect(p.hasConflict, isFalse);
79+
expect(p.worksAnywhere, isTrue);
80+
expect(p.worksEverywhere, isFalse);
81+
expect(p.worksInBrowser, isTrue);
82+
expect(p.worksInConsole, isFalse);
83+
expect(p.worksInFlutter, isFalse);
84+
});
85+
86+
test('detect angular', () {
87+
Platform p =
88+
classifyPlatform(['dart:html', 'package:angular2/angular2.dart']);
89+
expect(p.hasConflict, isFalse);
90+
expect(p.worksAnywhere, isTrue);
91+
expect(p.worksEverywhere, isFalse);
92+
expect(p.worksInBrowser, isTrue);
93+
expect(p.worksInConsole, isFalse);
94+
expect(p.worksInFlutter, isFalse);
95+
expect(p.uses, [KnownPlatforms.angular, KnownPlatforms.browser]);
96+
});
97+
});
98+
99+
group('Conflicting Platform', () {
100+
test('dart:html + dart:ui', () {
101+
Platform p = classifyPlatform(['dart:html', 'dart:ui']);
102+
expect(p.hasConflict, isTrue);
103+
expect(p.worksAnywhere, isFalse);
104+
expect(p.worksEverywhere, isFalse);
105+
expect(p.worksInBrowser, isFalse);
106+
expect(p.worksInConsole, isFalse);
107+
expect(p.worksInFlutter, isFalse);
108+
});
109+
110+
test('dart:mirrors + dart:ui', () {
111+
Platform p = classifyPlatform(['dart:mirrors', 'dart:ui']);
112+
expect(p.hasConflict, isTrue);
113+
expect(p.worksAnywhere, isFalse);
114+
expect(p.worksEverywhere, isFalse);
115+
expect(p.worksInBrowser, isFalse);
116+
expect(p.worksInConsole, isFalse);
117+
expect(p.worksInFlutter, isFalse);
118+
});
119+
});
120+
121+
group('PlatformSummary', () {
122+
test('handles multiple libraries', () {
123+
PlatformSummary sum = classifyPlatforms(emptyPubspec, {
124+
'package:_example/a.dart': ['dart:html'],
125+
'package:_example/b.dart': ['dart:io'],
126+
});
127+
expect(sum.hasConflict, isFalse);
128+
expect(sum.package.worksEverywhere, isTrue);
129+
expect(sum.libraries.length, 2);
130+
Platform pa = sum.libraries['package:_example/a.dart'];
131+
Platform pb = sum.libraries['package:_example/b.dart'];
132+
expect(pa.worksInBrowser, isTrue);
133+
expect(pa.worksInConsole, isFalse);
134+
expect(pb.worksInBrowser, isFalse);
135+
expect(pb.worksInConsole, isTrue);
136+
});
137+
138+
test('detects flutter in pubspec', () {
139+
PlatformSummary sum = classifyPlatforms(flutterPluginPubspec, {});
140+
expect(sum.hasConflict, isFalse);
141+
expect(sum.package.worksInFlutter, isTrue);
142+
expect(sum.package.worksInConsole, isFalse);
143+
expect(sum.package.worksInBrowser, isFalse);
144+
});
145+
146+
test('detects flutter in dependencies', () {
147+
PlatformSummary sum = classifyPlatforms(flutterSdkPubspec, {});
148+
expect(sum.hasConflict, isFalse);
149+
expect(sum.package.worksInFlutter, isTrue);
150+
expect(sum.package.worksInConsole, isFalse);
151+
expect(sum.package.worksInBrowser, isFalse);
152+
});
153+
});
154+
155+
group('Conflicting PlatformSummary', () {
156+
test('Flutter package with mirrors', () {
157+
PlatformSummary sum = classifyPlatforms(flutterPluginPubspec, {
158+
'package:_example/lib.dart': ['dart:mirrors'],
159+
});
160+
expect(sum.hasConflict, isTrue);
161+
});
162+
});
163+
}

test/pubspec_test.dart

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:pana/src/pubspec.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() {
5+
test('empty', () {
6+
expect(emptyPubspec.hasFlutterKey, isFalse);
7+
expect(emptyPubspec.hasFlutterPluginKey, isFalse);
8+
expect(emptyPubspec.dependsOnFlutterSdk, isFalse);
9+
expect(emptyPubspec.dependsOnAnyVersion, isFalse);
10+
});
11+
12+
test('flutter', () {
13+
expect(flutterPluginPubspec.hasFlutterKey, isTrue);
14+
expect(flutterPluginPubspec.hasFlutterPluginKey, isTrue);
15+
expect(flutterPluginPubspec.dependsOnFlutterSdk, isFalse);
16+
expect(flutterPluginPubspec.dependsOnAnyVersion, isFalse);
17+
expect(flutterSdkPubspec.hasFlutterKey, isFalse);
18+
expect(flutterSdkPubspec.hasFlutterPluginKey, isFalse);
19+
expect(flutterSdkPubspec.dependsOnFlutterSdk, isTrue);
20+
expect(flutterSdkPubspec.dependsOnAnyVersion, isFalse);
21+
});
22+
23+
test('detect any', () {
24+
expect(futureProofPubspec1.dependsOnAnyVersion, isTrue);
25+
expect(futureProofPubspec2.dependsOnAnyVersion, isTrue);
26+
});
27+
}
28+
29+
final Pubspec emptyPubspec = new Pubspec({});
30+
31+
final Pubspec flutterPluginPubspec = new Pubspec({
32+
'flutter': {
33+
'plugin': {},
34+
},
35+
});
36+
37+
final Pubspec flutterSdkPubspec = new Pubspec({
38+
'dependencies': {
39+
'example': {
40+
'sdk': 'flutter',
41+
},
42+
},
43+
});
44+
45+
final Pubspec futureProofPubspec1 = new Pubspec({
46+
'dependencies': {
47+
'http': 'any',
48+
},
49+
});
50+
51+
final Pubspec futureProofPubspec2 = new Pubspec({
52+
'dependencies': {
53+
'http': {
54+
'version': 'any',
55+
},
56+
},
57+
});

0 commit comments

Comments
 (0)