mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Switch flutter_tools to use frontend_server for web compilation (#50365)
This commit is contained in:
parent
324e20de09
commit
18f38cd45b
@ -20,7 +20,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -73,4 +73,4 @@ flutter:
|
||||
assets:
|
||||
- icon/
|
||||
|
||||
# PUBSPEC CHECKSUM: 43e1
|
||||
# PUBSPEC CHECKSUM: 7ae2
|
||||
|
@ -50,7 +50,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -92,4 +92,4 @@ flutter:
|
||||
- packages/flutter_gallery_assets/people/square/ali.png
|
||||
- packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png
|
||||
|
||||
# PUBSPEC CHECKSUM: 6a4f
|
||||
# PUBSPEC CHECKSUM: a450
|
||||
|
@ -50,7 +50,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -93,4 +93,4 @@ flutter:
|
||||
- packages/flutter_gallery_assets/food/cherry_pie.png
|
||||
- assets/999x1000.png
|
||||
|
||||
# PUBSPEC CHECKSUM: 6a4f
|
||||
# PUBSPEC CHECKSUM: a450
|
||||
|
@ -24,7 +24,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
dart_style: 1.3.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -78,4 +78,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 053c
|
||||
# PUBSPEC CHECKSUM: c23d
|
||||
|
@ -53,7 +53,7 @@ dev_dependencies:
|
||||
|
||||
archive: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_multi_server: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
image: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -87,4 +87,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 5272
|
||||
# PUBSPEC CHECKSUM: bc73
|
||||
|
@ -26,7 +26,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -69,4 +69,4 @@ dev_dependencies:
|
||||
mockito: 4.1.1
|
||||
test_api: 0.2.11
|
||||
|
||||
# PUBSPEC CHECKSUM: 26ab
|
||||
# PUBSPEC CHECKSUM: abac
|
||||
|
@ -44,7 +44,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -76,4 +76,4 @@ dev_dependencies:
|
||||
watcher: 0.9.7+13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
yaml: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
# PUBSPEC CHECKSUM: d160
|
||||
# PUBSPEC CHECKSUM: 8b61
|
||||
|
@ -17,7 +17,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -70,4 +70,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -50,7 +50,7 @@ dev_dependencies:
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -139,4 +139,4 @@ flutter:
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
|
||||
# PUBSPEC CHECKSUM: c1f5
|
||||
# PUBSPEC CHECKSUM: e9f6
|
||||
|
@ -50,7 +50,7 @@ dev_dependencies:
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -139,4 +139,4 @@ flutter:
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
|
||||
# PUBSPEC CHECKSUM: c1f5
|
||||
# PUBSPEC CHECKSUM: e9f6
|
||||
|
@ -48,7 +48,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -87,4 +87,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: a1cf
|
||||
# PUBSPEC CHECKSUM: e3d0
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -74,4 +74,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -42,7 +42,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -81,4 +81,4 @@ builders:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -74,4 +74,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -74,4 +74,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -43,7 +43,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -80,4 +80,4 @@ flutter:
|
||||
assets:
|
||||
- assets/
|
||||
|
||||
# PUBSPEC CHECKSUM: ec3a
|
||||
# PUBSPEC CHECKSUM: d23b
|
||||
|
@ -15,7 +15,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -62,4 +62,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: f8e0
|
||||
# PUBSPEC CHECKSUM: e1e1
|
||||
|
@ -21,7 +21,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -74,4 +74,4 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 7669
|
||||
# PUBSPEC CHECKSUM: 086a
|
||||
|
@ -17,7 +17,7 @@ dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
e2e: 0.2.3
|
||||
e2e: 0.2.3+1
|
||||
|
||||
archive: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
args: 1.5.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -40,4 +40,4 @@ dev_dependencies:
|
||||
test_api: 0.2.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
xml: 3.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
# PUBSPEC CHECKSUM: b7d2
|
||||
# PUBSPEC CHECKSUM: bb2f
|
||||
|
@ -22,7 +22,7 @@ dependencies:
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
collection: 1.14.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -83,4 +83,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 4450
|
||||
# PUBSPEC CHECKSUM: 4051
|
||||
|
@ -46,7 +46,7 @@ dev_dependencies:
|
||||
test: 1.9.4
|
||||
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http: 0.12.0+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_multi_server: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
http_parser: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -100,4 +100,4 @@ executables:
|
||||
vm_service_client: 0.2.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
web_socket_channel: 1.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
# PUBSPEC CHECKSUM: 59ea
|
||||
# PUBSPEC CHECKSUM: 42eb
|
||||
|
@ -36,7 +36,7 @@ dev_dependencies:
|
||||
_fe_analyzer_shared: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
analyzer: 0.39.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
html: 0.14.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -68,4 +68,4 @@ dev_dependencies:
|
||||
web_socket_channel: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
yaml: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
# PUBSPEC CHECKSUM: fbaa
|
||||
# PUBSPEC CHECKSUM: 2bab
|
||||
|
@ -30,7 +30,7 @@ dev_dependencies:
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -83,4 +83,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 4450
|
||||
# PUBSPEC CHECKSUM: 4051
|
||||
|
@ -52,7 +52,7 @@ dev_dependencies:
|
||||
async: 2.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -271,4 +271,4 @@ flutter:
|
||||
- asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf
|
||||
- asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf
|
||||
|
||||
# PUBSPEC CHECKSUM: 5f11
|
||||
# PUBSPEC CHECKSUM: 3212
|
||||
|
@ -28,7 +28,7 @@ dev_dependencies:
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -82,4 +82,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 4450
|
||||
# PUBSPEC CHECKSUM: 4051
|
||||
|
@ -28,7 +28,7 @@ dev_dependencies:
|
||||
boolean_selector: 1.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
charcode: 1.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
coverage: 0.13.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
crypto: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
csslib: 0.16.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
file: 5.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -82,4 +82,4 @@ dev_dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 4450
|
||||
# PUBSPEC CHECKSUM: 4051
|
||||
|
@ -4,18 +4,14 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:build_daemon/client.dart';
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vmservice;
|
||||
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
|
||||
hide StackTrace;
|
||||
|
||||
import '../application_package.dart';
|
||||
import '../base/async_guard.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/net.dart';
|
||||
import '../base/terminal.dart';
|
||||
@ -34,10 +30,10 @@ import '../reporting/reporting.dart';
|
||||
import '../resident_runner.dart';
|
||||
import '../run_hot.dart';
|
||||
import '../web/chrome.dart';
|
||||
import '../web/compile.dart';
|
||||
import '../web/devfs_web.dart';
|
||||
import '../web/web_device.dart';
|
||||
import '../web/web_runner.dart';
|
||||
import 'web_fs.dart';
|
||||
|
||||
/// Injectable factory to create a [ResidentWebRunner].
|
||||
class DwdsWebRunnerFactory extends WebRunnerFactory {
|
||||
@ -52,19 +48,7 @@ class DwdsWebRunnerFactory extends WebRunnerFactory {
|
||||
@required List<String> dartDefines,
|
||||
@required UrlTunneller urlTunneller,
|
||||
}) {
|
||||
if (featureFlags.isWebIncrementalCompilerEnabled && debuggingOptions.buildInfo.isDebug) {
|
||||
return _ExperimentalResidentWebRunner(
|
||||
device,
|
||||
target: target,
|
||||
flutterProject: flutterProject,
|
||||
debuggingOptions: debuggingOptions,
|
||||
ipv6: ipv6,
|
||||
stayResident: stayResident,
|
||||
dartDefines: dartDefines,
|
||||
// TODO(dantup): If this becomes default it may need to urlTunneller.
|
||||
);
|
||||
}
|
||||
return _DwdsResidentWebRunner(
|
||||
return _ResidentWebRunner(
|
||||
device,
|
||||
target: target,
|
||||
flutterProject: flutterProject,
|
||||
@ -116,7 +100,6 @@ abstract class ResidentWebRunner extends ResidentRunner {
|
||||
|
||||
bool get _enableDwds => debuggingEnabled;
|
||||
|
||||
WebFs _webFs;
|
||||
ConnectionResult _connectionResult;
|
||||
StreamSubscription<vmservice.Event> _stdOutSub;
|
||||
bool _exited = false;
|
||||
@ -155,9 +138,15 @@ abstract class ResidentWebRunner extends ResidentRunner {
|
||||
return;
|
||||
}
|
||||
await _stdOutSub?.cancel();
|
||||
await _webFs?.stop();
|
||||
await device.device.stopApp(null);
|
||||
_generatedEntrypointDirectory?.deleteSync(recursive: true);
|
||||
try {
|
||||
_generatedEntrypointDirectory?.deleteSync(recursive: true);
|
||||
} on FileSystemException {
|
||||
// Best effort to clean up temp dirs.
|
||||
globals.printTrace(
|
||||
'Failed to clean up temp directory: ${_generatedEntrypointDirectory.path}',
|
||||
);
|
||||
}
|
||||
if (ChromeLauncher.hasChromeInstance) {
|
||||
final Chrome chrome = await ChromeLauncher.connectedInstance;
|
||||
await chrome.close();
|
||||
@ -348,8 +337,8 @@ abstract class ResidentWebRunner extends ResidentRunner {
|
||||
}
|
||||
}
|
||||
|
||||
class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
_ExperimentalResidentWebRunner(
|
||||
class _ResidentWebRunner extends ResidentWebRunner {
|
||||
_ResidentWebRunner(
|
||||
FlutterDevice device, {
|
||||
String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@ -357,6 +346,7 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
@required DebuggingOptions debuggingOptions,
|
||||
bool stayResident = true,
|
||||
@required List<String> dartDefines,
|
||||
@required this.urlTunneller,
|
||||
}) : super(
|
||||
device,
|
||||
flutterProject: flutterProject,
|
||||
@ -367,8 +357,7 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
dartDefines: dartDefines,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get debuggingEnabled => false;
|
||||
final UrlTunneller urlTunneller;
|
||||
|
||||
@override
|
||||
Future<int> run({
|
||||
@ -405,13 +394,31 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
? await globals.os.findFreePort()
|
||||
: int.tryParse(debuggingOptions.port);
|
||||
device.devFS = WebDevFS(
|
||||
effectiveHostname,
|
||||
hostPort,
|
||||
packagesFilePath,
|
||||
hostname: effectiveHostname,
|
||||
port: hostPort,
|
||||
packagesFilePath: packagesFilePath,
|
||||
urlTunneller: urlTunneller,
|
||||
buildMode: debuggingOptions.buildInfo.mode,
|
||||
enableDwds: _enableDwds,
|
||||
);
|
||||
final Uri url = await device.devFS.create();
|
||||
await _updateDevFS(fullRestart: true);
|
||||
device.generator.accept();
|
||||
if (debuggingOptions.buildInfo.isDebug) {
|
||||
final UpdateFSReport report = await _updateDevFS(fullRestart: true);
|
||||
if (!report.success) {
|
||||
globals.printError('Failed to compile application.');
|
||||
return 1;
|
||||
}
|
||||
device.generator.accept();
|
||||
} else {
|
||||
await buildWeb(
|
||||
flutterProject,
|
||||
target,
|
||||
debuggingOptions.buildInfo,
|
||||
debuggingOptions.initializePlatform,
|
||||
dartDefines,
|
||||
false,
|
||||
);
|
||||
}
|
||||
await device.device.startApp(
|
||||
package,
|
||||
mainPath: target,
|
||||
@ -442,18 +449,39 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
progressId: 'hot.restart',
|
||||
);
|
||||
|
||||
final UpdateFSReport report = await _updateDevFS(fullRestart: fullRestart);
|
||||
if (report.success) {
|
||||
device.generator.accept();
|
||||
String reloadModules;
|
||||
if (debuggingOptions.buildInfo.isDebug) {
|
||||
// Full restart is always false for web, since the extra recompile is wasteful.
|
||||
final UpdateFSReport report = await _updateDevFS(fullRestart: false);
|
||||
if (report.success) {
|
||||
device.generator.accept();
|
||||
} else {
|
||||
status.stop();
|
||||
await device.generator.reject();
|
||||
return OperationResult(1, 'Failed to recompile application.');
|
||||
}
|
||||
reloadModules = report.invalidatedModules
|
||||
.map((String module) => '"$module"')
|
||||
.join(',');
|
||||
} else {
|
||||
await device.generator.reject();
|
||||
try {
|
||||
await buildWeb(
|
||||
flutterProject,
|
||||
target,
|
||||
debuggingOptions.buildInfo,
|
||||
debuggingOptions.initializePlatform,
|
||||
dartDefines,
|
||||
false,
|
||||
);
|
||||
} on ToolExit {
|
||||
return OperationResult(1, 'Failed to recompile application.');
|
||||
}
|
||||
}
|
||||
final String modules = report.invalidatedModules
|
||||
.map((String module) => '"$module"')
|
||||
.join(',');
|
||||
|
||||
try {
|
||||
if (fullRestart || !debuggingOptions.buildInfo.isDebug) {
|
||||
if (!deviceIsDebuggable) {
|
||||
globals.printStatus('Recompile complete. Page requires refresh.');
|
||||
} else if (fullRestart || !debuggingOptions.buildInfo.isDebug) {
|
||||
// On non-debug builds, a hard refresh is required to ensure the
|
||||
// up to date sources are loaded.
|
||||
await _wipConnection?.sendCommand('Page.reload', <String, Object>{
|
||||
@ -462,7 +490,7 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
} else {
|
||||
await _wipConnection?.debugger
|
||||
?.sendCommand('Runtime.evaluate', params: <String, Object>{
|
||||
'expression': 'window.\$hotReloadHook([$modules])',
|
||||
'expression': 'window.\$hotReloadHook([$reloadModules])',
|
||||
'awaitPromise': true,
|
||||
'returnByValue': true,
|
||||
});
|
||||
@ -473,19 +501,23 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
} finally {
|
||||
status.stop();
|
||||
}
|
||||
|
||||
final String verb = fullRestart ? 'Restarted' : 'Reloaded';
|
||||
globals.printStatus('$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
|
||||
if (!fullRestart) {
|
||||
|
||||
// Don't track restart times for dart2js builds or web-server devices.
|
||||
if (debuggingOptions.buildInfo.isDebug && deviceIsDebuggable) {
|
||||
flutterUsage.sendTiming('hot', 'web-incremental-restart', timer.elapsed);
|
||||
HotEvent(
|
||||
'restart',
|
||||
targetPlatform: getNameForTargetPlatform(TargetPlatform.web_javascript),
|
||||
sdkName: await device.device.sdkNameAndVersion,
|
||||
emulator: false,
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
overallTimeInMs: timer.elapsed.inMilliseconds,
|
||||
).send();
|
||||
}
|
||||
HotEvent(
|
||||
'restart',
|
||||
targetPlatform: getNameForTargetPlatform(TargetPlatform.web_javascript),
|
||||
sdkName: await device.device.sdkNameAndVersion,
|
||||
emulator: false,
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
).send();
|
||||
return OperationResult.ok;
|
||||
}
|
||||
|
||||
@ -509,9 +541,10 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
.childFile('generated_plugin_registrant.dart')
|
||||
.absolute.path;
|
||||
final Uri generatedImport = packageUriMapper.map(generatedPath);
|
||||
final String importedEntrypoint = packageUriMapper.map(main).toString() ?? 'file://$main';
|
||||
|
||||
final String entrypoint = <String>[
|
||||
'import "${packageUriMapper.map(main)}" as entrypoint;',
|
||||
'import "$importedEntrypoint" as entrypoint;',
|
||||
'import "dart:ui" as ui;',
|
||||
if (hasWebPlugins)
|
||||
'import "package:flutter_web_plugins/flutter_web_plugins.dart";',
|
||||
@ -583,259 +616,13 @@ class _ExperimentalResidentWebRunner extends ResidentWebRunner {
|
||||
}
|
||||
_wipConnection = await chromeTab.connect();
|
||||
}
|
||||
appStartedCompleter?.complete();
|
||||
connectionInfoCompleter?.complete(DebugConnectionInfo());
|
||||
if (stayResident) {
|
||||
await waitForAppToFinish();
|
||||
} else {
|
||||
await stopEchoingDeviceLog();
|
||||
await exitApp();
|
||||
}
|
||||
await cleanupAtFinish();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class _DwdsResidentWebRunner extends ResidentWebRunner {
|
||||
_DwdsResidentWebRunner(
|
||||
FlutterDevice device, {
|
||||
String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@required bool ipv6,
|
||||
@required DebuggingOptions debuggingOptions,
|
||||
@required this.urlTunneller,
|
||||
bool stayResident = true,
|
||||
@required List<String> dartDefines,
|
||||
}) : super(
|
||||
device,
|
||||
flutterProject: flutterProject,
|
||||
target: target ?? globals.fs.path.join('lib', 'main.dart'),
|
||||
debuggingOptions: debuggingOptions,
|
||||
ipv6: ipv6,
|
||||
stayResident: stayResident,
|
||||
dartDefines: dartDefines,
|
||||
);
|
||||
|
||||
UrlTunneller urlTunneller;
|
||||
|
||||
@override
|
||||
Future<int> run({
|
||||
Completer<DebugConnectionInfo> connectionInfoCompleter,
|
||||
Completer<void> appStartedCompleter,
|
||||
String route,
|
||||
}) async {
|
||||
firstBuildTime = DateTime.now();
|
||||
final ApplicationPackage package = await ApplicationPackageFactory.instance.getPackageForPlatform(
|
||||
TargetPlatform.web_javascript,
|
||||
applicationBinary: null,
|
||||
);
|
||||
if (package == null) {
|
||||
globals.printError('This application is not configured to build on the web.');
|
||||
globals.printError('To add web support to a project, run `flutter create .`.');
|
||||
return 1;
|
||||
}
|
||||
if (!globals.fs.isFileSync(mainPath)) {
|
||||
String message = 'Tried to run $mainPath, but that file does not exist.';
|
||||
if (target == null) {
|
||||
message +=
|
||||
'\nConsider using the -t option to specify the Dart file to start.';
|
||||
}
|
||||
globals.printError(message);
|
||||
return 1;
|
||||
}
|
||||
final String modeName = debuggingOptions.buildInfo.friendlyModeName;
|
||||
globals.printStatus(
|
||||
'Launching ${globals.fsUtils.getDisplayPath(target)} '
|
||||
'on ${device.device.name} in $modeName mode...',
|
||||
);
|
||||
Status buildStatus;
|
||||
bool statusActive = false;
|
||||
try {
|
||||
// dwds does not handle uncaught exceptions from its servers. To work
|
||||
// around this, we need to catch all uncaught exceptions and determine if
|
||||
// they are fatal or not.
|
||||
buildStatus = globals.logger.startProgress('Building application for the web...', timeout: null);
|
||||
statusActive = true;
|
||||
final int result = await asyncGuard(() async {
|
||||
_webFs = await webFsFactory(
|
||||
target: target,
|
||||
flutterProject: flutterProject,
|
||||
buildInfo: debuggingOptions.buildInfo,
|
||||
initializePlatform: debuggingOptions.initializePlatform,
|
||||
hostname: debuggingOptions.hostname,
|
||||
port: debuggingOptions.port,
|
||||
urlTunneller: urlTunneller,
|
||||
skipDwds: !_enableDwds,
|
||||
dartDefines: dartDefines,
|
||||
);
|
||||
// When connecting to a browser, update the message with a seemsSlow notification
|
||||
// to handle the case where we fail to connect.
|
||||
buildStatus.stop();
|
||||
statusActive = false;
|
||||
if (supportsServiceProtocol) {
|
||||
buildStatus = globals.logger.startProgress(
|
||||
'Attempting to connect to browser instance..',
|
||||
timeout: const Duration(seconds: 30),
|
||||
);
|
||||
statusActive = true;
|
||||
}
|
||||
await device.device.startApp(
|
||||
package,
|
||||
mainPath: target,
|
||||
debuggingOptions: debuggingOptions,
|
||||
platformArgs: <String, Object>{
|
||||
'uri': _webFs.uri,
|
||||
},
|
||||
);
|
||||
if (_enableDwds) {
|
||||
final bool useDebugExtension = device.device is WebServerDevice && debuggingOptions.startPaused;
|
||||
_connectionResult = await _webFs.connect(useDebugExtension);
|
||||
unawaited(_connectionResult.debugConnection.onDone.whenComplete(_cleanupAndExit));
|
||||
}
|
||||
if (statusActive) {
|
||||
buildStatus.stop();
|
||||
statusActive = false;
|
||||
}
|
||||
appStartedCompleter?.complete();
|
||||
return attach(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
appStartedCompleter: appStartedCompleter,
|
||||
);
|
||||
});
|
||||
return result;
|
||||
} on VersionSkew {
|
||||
// Thrown if an older build daemon is already running.
|
||||
throwToolExit(
|
||||
'Another build daemon is already running with an older version.\n'
|
||||
'Try exiting other Flutter processes in this project and try again.');
|
||||
} on OptionsSkew {
|
||||
// Thrown if a build daemon is already running with different configuration.
|
||||
throwToolExit(
|
||||
'Another build daemon is already running with different configuration.\n'
|
||||
'Exit other Flutter processes running in this project and try again.');
|
||||
} on WebSocketException {
|
||||
throwToolExit('Failed to connect to WebSocket.');
|
||||
} on BuildException {
|
||||
throwToolExit('Failed to build application for the Web.');
|
||||
} on ChromeDebugException catch (err, stackTrace) {
|
||||
throwToolExit(
|
||||
'Failed to establish connection with Chrome. Try running the application again.\n'
|
||||
'If this problem persists, please file an issue with the details below:\n$err\n$stackTrace');
|
||||
} on AppConnectionException {
|
||||
throwToolExit(
|
||||
'Failed to establish connection with the application instance in Chrome.\n'
|
||||
'This can happen if the websocket connection used by the web tooling is '
|
||||
'unabled to correctly establish a connection, for example due to a firewall.');
|
||||
} on MissingPortFile {
|
||||
throwToolExit(
|
||||
'Failed to connect to build daemon.\nThe daemon either failed to '
|
||||
'start or was killed by another process.');
|
||||
} on SocketException catch (err) {
|
||||
throwToolExit(err.toString());
|
||||
} on StateError catch (err) {
|
||||
final String message = err.toString();
|
||||
if (message.contains('Unable to start build daemon')) {
|
||||
throwToolExit('Failed to start build daemon. The process might have '
|
||||
'exited unexpectedly during startup. Try running the application '
|
||||
'again.');
|
||||
}
|
||||
rethrow;
|
||||
} finally {
|
||||
if (statusActive) {
|
||||
buildStatus.stop();
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<OperationResult> restart({
|
||||
bool fullRestart = false,
|
||||
bool pause = false,
|
||||
String reason,
|
||||
bool benchmarkMode = false,
|
||||
}) async {
|
||||
final Stopwatch timer = Stopwatch()..start();
|
||||
final Status status = globals.logger.startProgress(
|
||||
'Performing hot restart...',
|
||||
timeout: supportsServiceProtocol
|
||||
? timeoutConfiguration.fastOperation
|
||||
: timeoutConfiguration.slowOperation,
|
||||
progressId: 'hot.restart',
|
||||
);
|
||||
final bool success = await _webFs.recompile();
|
||||
if (!success) {
|
||||
status.stop();
|
||||
return OperationResult(1, 'Failed to recompile application.');
|
||||
}
|
||||
if (supportsServiceProtocol) {
|
||||
// Send an event for only recompilation.
|
||||
final Duration recompileDuration = timer.elapsed;
|
||||
flutterUsage.sendTiming('hot', 'web-recompile', recompileDuration);
|
||||
try {
|
||||
final vmservice.Response reloadResponse = fullRestart
|
||||
? await _vmService.callServiceExtension('fullReload')
|
||||
: await _vmService.callServiceExtension('hotRestart');
|
||||
final String verb = fullRestart ? 'Restarted' : 'Reloaded';
|
||||
globals.printStatus(
|
||||
'$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
|
||||
|
||||
// Send timing analytics for full restart and for refresh.
|
||||
final bool wasSuccessful = reloadResponse.type == 'Success';
|
||||
if (!wasSuccessful) {
|
||||
return OperationResult(1, reloadResponse.toString());
|
||||
}
|
||||
if (!fullRestart) {
|
||||
flutterUsage.sendTiming('hot', 'web-restart', timer.elapsed);
|
||||
flutterUsage.sendTiming('hot', 'web-refresh', timer.elapsed - recompileDuration);
|
||||
}
|
||||
return OperationResult.ok;
|
||||
} on vmservice.RPCError {
|
||||
return OperationResult(1, 'Page requires refresh.');
|
||||
} finally {
|
||||
status.stop();
|
||||
HotEvent(
|
||||
'restart',
|
||||
targetPlatform: getNameForTargetPlatform(TargetPlatform.web_javascript),
|
||||
sdkName: await device.device.sdkNameAndVersion,
|
||||
emulator: false,
|
||||
fullRestart: true,
|
||||
reason: reason,
|
||||
).send();
|
||||
}
|
||||
}
|
||||
// Allows browser refresh hot restart on non-debug builds.
|
||||
if (device.device is ChromeDevice && !isRunningDebug) {
|
||||
try {
|
||||
final Chrome chrome = await ChromeLauncher.connectedInstance;
|
||||
final ChromeTab chromeTab = await chrome.chromeConnection.getTab((ChromeTab chromeTab) {
|
||||
return chromeTab.url.contains(debuggingOptions.hostname);
|
||||
});
|
||||
final WipConnection wipConnection = await chromeTab.connect();
|
||||
// On non-debug builds, a hard refresh is required to ensure the
|
||||
// up to date sources are loaded.
|
||||
await wipConnection?.sendCommand('Page.reload', <String, Object>{
|
||||
'ignoreCache': !debuggingOptions.buildInfo.isDebug,
|
||||
});
|
||||
status.stop();
|
||||
return OperationResult.ok;
|
||||
} catch (err) {
|
||||
globals.printTrace(err.toString());
|
||||
// Ignore error and continue with posted message;
|
||||
}
|
||||
}
|
||||
status.stop();
|
||||
globals.printStatus('Recompile complete. Page requires refresh.');
|
||||
return OperationResult.ok;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> attach({
|
||||
Completer<DebugConnectionInfo> connectionInfoCompleter,
|
||||
Completer<void> appStartedCompleter,
|
||||
}) async {
|
||||
Uri websocketUri;
|
||||
if (supportsServiceProtocol) {
|
||||
final WebDevFS webDevFS = device.devFS as WebDevFS;
|
||||
final bool useDebugExtension = device.device is WebServerDevice && debuggingOptions.startPaused;
|
||||
_connectionResult = await webDevFS.connect(useDebugExtension);
|
||||
unawaited(_connectionResult.debugConnection.onDone.whenComplete(_cleanupAndExit));
|
||||
|
||||
// Cleanup old subscriptions. These will throw if there isn't anything
|
||||
// listening, which is fine because that is what we want to ensure.
|
||||
try {
|
||||
@ -855,6 +642,14 @@ class _DwdsResidentWebRunner extends ResidentWebRunner {
|
||||
globals.printStatus(message);
|
||||
});
|
||||
unawaited(_vmService.registerService('reloadSources', 'FlutterTools'));
|
||||
_vmService.registerServiceCallback('reloadSources', (Map<String, Object> params) async {
|
||||
final bool pause = params['pause'] as bool ?? false;
|
||||
await restart(benchmarkMode: false, pause: pause, fullRestart: false);
|
||||
return <String, Object>{'type': 'Success'};
|
||||
});
|
||||
// Note: can't register our own hot restart hook. Would be fixed by moving
|
||||
// to DWDS digests.
|
||||
|
||||
websocketUri = Uri.parse(_connectionResult.debugConnection.uri);
|
||||
// Always run main after connecting because start paused doesn't work yet.
|
||||
if (!debuggingOptions.startPaused || !supportsServiceProtocol) {
|
||||
@ -873,8 +668,8 @@ class _DwdsResidentWebRunner extends ResidentWebRunner {
|
||||
if (websocketUri != null) {
|
||||
globals.printStatus('Debug service listening on $websocketUri');
|
||||
}
|
||||
appStartedCompleter?.complete();
|
||||
connectionInfoCompleter?.complete(DebugConnectionInfo(wsUri: websocketUri));
|
||||
|
||||
if (stayResident) {
|
||||
await waitForAppToFinish();
|
||||
} else {
|
||||
|
@ -2,26 +2,25 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: implementation_imports
|
||||
import 'dart:async';
|
||||
import 'dart:io' as io; // ignore: dart_io_import
|
||||
|
||||
import 'package:build/build.dart';
|
||||
import 'package:build_daemon/client.dart';
|
||||
import 'package:build_daemon/constants.dart' as daemon;
|
||||
import 'package:build_daemon/data/build_status.dart';
|
||||
import 'package:build_runner_core/build_runner_core.dart' as core;
|
||||
import 'package:glob/glob.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:build_daemon/data/build_target.dart';
|
||||
import 'package:build_daemon/data/server_log.dart';
|
||||
import 'package:path/path.dart' as path; // ignore: package_path_import
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../build_info.dart';
|
||||
import '../convert.dart';
|
||||
import '../cache.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../platform_plugins.dart';
|
||||
import '../plugins.dart';
|
||||
import '../project.dart';
|
||||
import '../web/compile.dart';
|
||||
import 'web_fs.dart';
|
||||
|
||||
/// A build_runner specific implementation of the [WebCompilationProxy].
|
||||
class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
@ -42,8 +41,8 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
.createSync();
|
||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(projectDirectory);
|
||||
final bool hasWebPlugins = findPlugins(flutterProject)
|
||||
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
|
||||
final BuildDaemonClient client = await buildDaemonCreator.startBuildDaemon(
|
||||
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
|
||||
final BuildDaemonClient client = await const BuildDaemonCreator().startBuildDaemon(
|
||||
projectDirectory.path,
|
||||
release: mode == BuildMode.release,
|
||||
profile: mode == BuildMode.profile,
|
||||
@ -63,6 +62,9 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
await for (final BuildResults results in client.buildResults) {
|
||||
final BuildResult result = results.results.firstWhere((BuildResult result) {
|
||||
return result.target == 'web';
|
||||
}, orElse: () {
|
||||
// Assume build failed if we lack any results.
|
||||
return DefaultBuildResult((DefaultBuildResultBuilder b) => b.status == BuildStatus.failed);
|
||||
});
|
||||
if (result.status == BuildStatus.failed) {
|
||||
success = false;
|
||||
@ -72,119 +74,188 @@ class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success && testOutputDir != null) {
|
||||
final Directory rootDirectory = projectDirectory
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('build')
|
||||
.childDirectory('flutter_web');
|
||||
if (!success || testOutputDir == null) {
|
||||
return success;
|
||||
}
|
||||
final Directory rootDirectory = projectDirectory
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('build')
|
||||
.childDirectory('flutter_web');
|
||||
|
||||
final Iterable<Directory> childDirectories = rootDirectory
|
||||
.listSync()
|
||||
.whereType<Directory>();
|
||||
for (final Directory childDirectory in childDirectories) {
|
||||
final String path = globals.fs.path.join(
|
||||
testOutputDir,
|
||||
'packages',
|
||||
globals.fs.path.basename(childDirectory.path),
|
||||
);
|
||||
globals.fsUtils.copyDirectorySync(
|
||||
childDirectory.childDirectory('lib'),
|
||||
globals.fs.directory(path),
|
||||
);
|
||||
}
|
||||
final Directory outputDirectory = rootDirectory
|
||||
.childDirectory(projectName)
|
||||
.childDirectory('test');
|
||||
final Iterable<Directory> childDirectories = rootDirectory
|
||||
.listSync()
|
||||
.whereType<Directory>();
|
||||
for (final Directory childDirectory in childDirectories) {
|
||||
final String path = globals.fs.path.join(
|
||||
testOutputDir,
|
||||
'packages',
|
||||
globals.fs.path.basename(childDirectory.path),
|
||||
);
|
||||
globals.fsUtils.copyDirectorySync(
|
||||
outputDirectory,
|
||||
globals.fs.directory(globals.fs.path.join(testOutputDir)),
|
||||
childDirectory.childDirectory('lib'),
|
||||
globals.fs.directory(path),
|
||||
);
|
||||
}
|
||||
final Directory outputDirectory = rootDirectory
|
||||
.childDirectory(projectName)
|
||||
.childDirectory('test');
|
||||
globals.fsUtils.copyDirectorySync(
|
||||
outputDirectory,
|
||||
globals.fs.directory(globals.fs.path.join(testOutputDir)),
|
||||
);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles mapping a single root file scheme to a multi-root scheme.
|
||||
///
|
||||
/// This allows one build_runner build to read the output from a previous
|
||||
/// isolated build.
|
||||
class MultirootFileBasedAssetReader extends core.FileBasedAssetReader {
|
||||
MultirootFileBasedAssetReader(
|
||||
core.PackageGraph packageGraph,
|
||||
this.generatedDirectory,
|
||||
) : super(packageGraph);
|
||||
class WebTestTargetManifest {
|
||||
WebTestTargetManifest(this.buildFilters);
|
||||
|
||||
final Directory generatedDirectory;
|
||||
WebTestTargetManifest.all() : buildFilters = null;
|
||||
|
||||
@override
|
||||
Future<bool> canRead(AssetId id) {
|
||||
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
|
||||
return _generatedFile(id).exists();
|
||||
final List<String> buildFilters;
|
||||
|
||||
bool get hasBuildFilters => buildFilters != null && buildFilters.isNotEmpty;
|
||||
}
|
||||
|
||||
/// A testable interface for starting a build daemon.
|
||||
class BuildDaemonCreator {
|
||||
const BuildDaemonCreator();
|
||||
|
||||
// TODO(jonahwilliams): find a way to get build checks working for flutter for web.
|
||||
static const String _ignoredLine1 = 'Warning: Interpreting this as package URI';
|
||||
static const String _ignoredLine2 = 'build_script.dart was not found in the asset graph, incremental builds will not work';
|
||||
static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml';
|
||||
|
||||
/// Start a build daemon and register the web targets.
|
||||
///
|
||||
/// [initializePlatform] controls whether we should invoke [webOnlyInitializePlatform].
|
||||
Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {
|
||||
bool release = false,
|
||||
bool profile = false,
|
||||
bool hasPlugins = false,
|
||||
bool initializePlatform = true,
|
||||
WebTestTargetManifest testTargets,
|
||||
}) async {
|
||||
try {
|
||||
final BuildDaemonClient client = await _connectClient(
|
||||
workingDirectory,
|
||||
release: release,
|
||||
profile: profile,
|
||||
hasPlugins: hasPlugins,
|
||||
initializePlatform: initializePlatform,
|
||||
testTargets: testTargets,
|
||||
);
|
||||
_registerBuildTargets(client, testTargets);
|
||||
return client;
|
||||
} on OptionsSkew {
|
||||
throwToolExit(
|
||||
'Incompatible options with current running build daemon.\n\n'
|
||||
'Please stop other flutter_tool instances running in this directory '
|
||||
'before starting a new instance with these options.'
|
||||
);
|
||||
}
|
||||
return super.canRead(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<int>> readAsBytes(AssetId id) {
|
||||
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
|
||||
return _generatedFile(id).readAsBytes();
|
||||
}
|
||||
return super.readAsBytes(id);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> readAsString(AssetId id, {Encoding encoding}) {
|
||||
if (packageGraph[id.package] == packageGraph.root && _missingSource(id)) {
|
||||
return _generatedFile(id).readAsString();
|
||||
}
|
||||
return super.readAsString(id, encoding: encoding);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<AssetId> findAssets(Glob glob, {String package}) async* {
|
||||
if (package == null || packageGraph.root.name == package) {
|
||||
final String generatedRoot = globals.fs.path.join(generatedDirectory.path, packageGraph.root.name);
|
||||
await for (final io.FileSystemEntity entity in glob.list(followLinks: true, root: packageGraph.root.path)) {
|
||||
if (entity is io.File && _isNotHidden(entity) && !globals.fs.path.isWithin(generatedRoot, entity.path)) {
|
||||
yield _fileToAssetId(entity, packageGraph.root);
|
||||
void _registerBuildTargets(
|
||||
BuildDaemonClient client,
|
||||
WebTestTargetManifest testTargets,
|
||||
) {
|
||||
final OutputLocation outputLocation = OutputLocation((OutputLocationBuilder b) => b
|
||||
..output = ''
|
||||
..useSymlinks = true
|
||||
..hoist = false);
|
||||
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b
|
||||
..target = 'web'
|
||||
..outputLocation = outputLocation?.toBuilder()));
|
||||
if (testTargets != null) {
|
||||
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) {
|
||||
b.target = 'test';
|
||||
b.outputLocation = outputLocation?.toBuilder();
|
||||
if (testTargets.hasBuildFilters) {
|
||||
b.buildFilters.addAll(testTargets.buildFilters);
|
||||
}
|
||||
}
|
||||
if (!globals.fs.isDirectorySync(generatedRoot)) {
|
||||
return;
|
||||
}
|
||||
await for (final io.FileSystemEntity entity in glob.list(followLinks: true, root: generatedRoot)) {
|
||||
if (entity is io.File && _isNotHidden(entity)) {
|
||||
yield _fileToAssetId(entity, packageGraph.root, globals.fs.path.relative(generatedRoot), true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}));
|
||||
}
|
||||
yield* super.findAssets(glob, package: package);
|
||||
}
|
||||
|
||||
bool _isNotHidden(io.FileSystemEntity entity) {
|
||||
return !path.basename(entity.path).startsWith('._');
|
||||
}
|
||||
Future<BuildDaemonClient> _connectClient(
|
||||
String workingDirectory, {
|
||||
bool release,
|
||||
bool profile,
|
||||
bool hasPlugins,
|
||||
bool initializePlatform,
|
||||
WebTestTargetManifest testTargets,
|
||||
}) {
|
||||
final String flutterToolsPackages = globals.fs.path.join(
|
||||
Cache.flutterRoot,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'.packages',
|
||||
);
|
||||
final String buildScript = globals.fs.path.join(
|
||||
Cache.flutterRoot,
|
||||
'packages',
|
||||
'flutter_tools',
|
||||
'lib',
|
||||
'src',
|
||||
'build_runner',
|
||||
'build_script.dart',
|
||||
);
|
||||
final String flutterWebSdk = globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
|
||||
|
||||
bool _missingSource(AssetId id) {
|
||||
return !globals.fs.file(path.joinAll(<String>[packageGraph.root.path, ...id.pathSegments])).existsSync();
|
||||
}
|
||||
// On Windows we need to call the snapshot directly otherwise
|
||||
// the process will start in a disjoint cmd without access to
|
||||
// STDIO.
|
||||
final List<String> args = <String>[
|
||||
globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||
'--packages=$flutterToolsPackages',
|
||||
buildScript,
|
||||
'daemon',
|
||||
'--skip-build-script-check',
|
||||
'--define', 'flutter_tools:ddc=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:entrypoint=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:entrypoint=release=$release',
|
||||
'--define', 'flutter_tools:entrypoint=profile=$profile',
|
||||
'--define', 'flutter_tools:shell=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:shell=hasPlugins=$hasPlugins',
|
||||
'--define', 'flutter_tools:shell=initializePlatform=$initializePlatform',
|
||||
// The following will cause build runner to only build tests that were requested.
|
||||
if (testTargets != null && testTargets.hasBuildFilters)
|
||||
for (final String buildFilter in testTargets.buildFilters)
|
||||
'--build-filter=$buildFilter',
|
||||
];
|
||||
|
||||
File _generatedFile(AssetId id) {
|
||||
return globals.fs.file(
|
||||
path.joinAll(<String>[generatedDirectory.path, packageGraph.root.name, ...id.pathSegments])
|
||||
return BuildDaemonClient.connect(
|
||||
workingDirectory,
|
||||
args,
|
||||
logHandler: (ServerLog serverLog) {
|
||||
switch (serverLog.level) {
|
||||
case Level.SEVERE:
|
||||
case Level.SHOUT:
|
||||
// Ignore certain non-actionable messages on startup.
|
||||
if (serverLog.message.contains(_ignoredLine1) ||
|
||||
serverLog.message.contains(_ignoredLine2) ||
|
||||
serverLog.message.contains(_ignoredLine3)) {
|
||||
return;
|
||||
}
|
||||
globals.printError(serverLog.message);
|
||||
if (serverLog.error != null) {
|
||||
globals.printError(serverLog.error);
|
||||
}
|
||||
if (serverLog.stackTrace != null) {
|
||||
globals.printTrace(serverLog.stackTrace);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (serverLog.message.contains('Skipping compiling')) {
|
||||
globals.printError(serverLog.message);
|
||||
} else {
|
||||
globals.printTrace(serverLog.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
buildMode: daemon.BuildMode.Manual,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates an [AssetId] for [file], which is a part of [packageNode].
|
||||
AssetId _fileToAssetId(io.File file, core.PackageNode packageNode, [String root, bool generated = false]) {
|
||||
final String filePath = path.normalize(file.absolute.path);
|
||||
String relativePath;
|
||||
if (generated) {
|
||||
relativePath = filePath.substring(root.length + 2);
|
||||
} else {
|
||||
relativePath = path.relative(filePath, from: packageNode.path);
|
||||
}
|
||||
return AssetId(packageNode.name, relativePath);
|
||||
}
|
||||
}
|
||||
|
@ -1,722 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:build_daemon/client.dart';
|
||||
import 'package:build_daemon/constants.dart' as daemon;
|
||||
import 'package:build_daemon/data/build_status.dart';
|
||||
import 'package:build_daemon/data/build_target.dart';
|
||||
import 'package:build_daemon/data/server_log.dart';
|
||||
import 'package:dwds/asset_handler.dart';
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:http_multi_server/http_multi_server.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart' as shelf_io;
|
||||
import 'package:shelf_proxy/shelf_proxy.dart';
|
||||
import 'package:mime/mime.dart' as mime;
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../asset.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/net.dart';
|
||||
import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
import '../cache.dart';
|
||||
import '../dart/package_map.dart';
|
||||
import '../dart/pub.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../platform_plugins.dart';
|
||||
import '../plugins.dart';
|
||||
import '../project.dart';
|
||||
import '../web/chrome.dart';
|
||||
import '../web/compile.dart';
|
||||
|
||||
/// The name of the built web project.
|
||||
const String kBuildTargetName = 'web';
|
||||
|
||||
/// A factory for creating a [Dwds] instance.
|
||||
DwdsFactory get dwdsFactory => context.get<DwdsFactory>() ?? Dwds.start;
|
||||
|
||||
/// The [BuildDaemonCreator] instance.
|
||||
BuildDaemonCreator get buildDaemonCreator => context.get<BuildDaemonCreator>() ?? const BuildDaemonCreator();
|
||||
|
||||
/// A factory for creating a [WebFs] instance.
|
||||
WebFsFactory get webFsFactory => context.get<WebFsFactory>() ?? WebFs.start;
|
||||
|
||||
/// A factory for creating an [HttpMultiServer] instance.
|
||||
HttpMultiServerFactory get httpMultiServerFactory => context.get<HttpMultiServerFactory>() ?? HttpMultiServer.bind;
|
||||
|
||||
/// A function with the same signature as [HttpMultiServer.bind].
|
||||
typedef HttpMultiServerFactory = Future<HttpServer> Function(dynamic address, int port);
|
||||
|
||||
/// A function with the same signature as [Dwds.start].
|
||||
typedef DwdsFactory = Future<Dwds> Function({
|
||||
@required AssetHandler assetHandler,
|
||||
@required Stream<BuildResult> buildResults,
|
||||
@required ConnectionProvider chromeConnection,
|
||||
String hostname,
|
||||
ReloadConfiguration reloadConfiguration,
|
||||
bool serveDevTools,
|
||||
LogWriter logWriter,
|
||||
bool verbose,
|
||||
bool enableDebugExtension,
|
||||
UrlEncoder urlEncoder,
|
||||
});
|
||||
|
||||
/// A function with the same signature as [WebFs.start].
|
||||
typedef WebFsFactory = Future<WebFs> Function({
|
||||
@required String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@required BuildInfo buildInfo,
|
||||
@required bool skipDwds,
|
||||
@required bool initializePlatform,
|
||||
@required String hostname,
|
||||
@required String port,
|
||||
@required UrlTunneller urlTunneller,
|
||||
@required List<String> dartDefines,
|
||||
});
|
||||
|
||||
/// The dev filesystem responsible for building and serving web applications.
|
||||
class WebFs {
|
||||
@visibleForTesting
|
||||
WebFs(
|
||||
this._client,
|
||||
this._server,
|
||||
this._dwds,
|
||||
this.uri,
|
||||
this._assetServer,
|
||||
this._useBuildRunner,
|
||||
this._flutterProject,
|
||||
this._target,
|
||||
this._buildInfo,
|
||||
this._initializePlatform,
|
||||
this._dartDefines,
|
||||
);
|
||||
|
||||
/// The server URL.
|
||||
final String uri;
|
||||
|
||||
final HttpServer _server;
|
||||
final Dwds _dwds;
|
||||
final BuildDaemonClient _client;
|
||||
final AssetServer _assetServer;
|
||||
final bool _useBuildRunner;
|
||||
final FlutterProject _flutterProject;
|
||||
final String _target;
|
||||
final BuildInfo _buildInfo;
|
||||
final bool _initializePlatform;
|
||||
final List<String> _dartDefines;
|
||||
StreamSubscription<void> _connectedApps;
|
||||
|
||||
static const String _kHostName = 'localhost';
|
||||
|
||||
Future<void> stop() async {
|
||||
await _client?.close();
|
||||
await _dwds?.stop();
|
||||
await _server.close(force: true);
|
||||
await _connectedApps?.cancel();
|
||||
_assetServer?.dispose();
|
||||
}
|
||||
|
||||
Future<DebugConnection> _cachedExtensionFuture;
|
||||
|
||||
/// Connect and retrieve the [DebugConnection] for the current application.
|
||||
///
|
||||
/// Only calls [AppConnection.runMain] on the subsequent connections.
|
||||
Future<ConnectionResult> connect(bool useDebugExtension) {
|
||||
final Completer<ConnectionResult> firstConnection = Completer<ConnectionResult>();
|
||||
_connectedApps = _dwds.connectedApps.listen((AppConnection appConnection) async {
|
||||
final DebugConnection debugConnection = useDebugExtension
|
||||
? await (_cachedExtensionFuture ??= _dwds.extensionDebugConnections.stream.first)
|
||||
: await _dwds.debugConnection(appConnection);
|
||||
if (!firstConnection.isCompleted) {
|
||||
firstConnection.complete(ConnectionResult(appConnection, debugConnection));
|
||||
} else {
|
||||
appConnection.runMain();
|
||||
}
|
||||
});
|
||||
return firstConnection.future;
|
||||
}
|
||||
|
||||
/// Recompile the web application and return whether this was successful.
|
||||
Future<bool> recompile() async {
|
||||
if (!_useBuildRunner) {
|
||||
await buildWeb(
|
||||
_flutterProject,
|
||||
_target,
|
||||
_buildInfo,
|
||||
_initializePlatform,
|
||||
_dartDefines,
|
||||
false,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
_client.startBuild();
|
||||
await for (final BuildResults results in _client.buildResults) {
|
||||
final BuildResult result = results.results.firstWhere((BuildResult result) {
|
||||
return result.target == kBuildTargetName;
|
||||
});
|
||||
if (result.status == BuildStatus.failed) {
|
||||
return false;
|
||||
}
|
||||
if (result.status == BuildStatus.succeeded) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Start the web compiler and asset server.
|
||||
static Future<WebFs> start({
|
||||
@required String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@required BuildInfo buildInfo,
|
||||
@required bool skipDwds,
|
||||
@required bool initializePlatform,
|
||||
@required String hostname,
|
||||
@required String port,
|
||||
@required UrlTunneller urlTunneller,
|
||||
@required List<String> dartDefines,
|
||||
}) async {
|
||||
// workaround for https://github.com/flutter/flutter/issues/38290
|
||||
if (!flutterProject.dartTool.existsSync()) {
|
||||
flutterProject.dartTool.createSync(recursive: true);
|
||||
}
|
||||
// Workaround for https://github.com/flutter/flutter/issues/41681.
|
||||
final String toolPath = globals.fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools');
|
||||
if (!globals.fs.isFileSync(globals.fs.path.join(toolPath, '.packages'))) {
|
||||
await pub.get(
|
||||
context: PubContext.pubGet,
|
||||
directory: toolPath,
|
||||
offline: true,
|
||||
skipPubspecYamlCheck: true,
|
||||
checkLastModified: false,
|
||||
);
|
||||
}
|
||||
|
||||
final Completer<bool> firstBuildCompleter = Completer<bool>();
|
||||
|
||||
// Initialize the asset bundle.
|
||||
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
||||
await assetBundle.build();
|
||||
await writeBundle(globals.fs.directory(getAssetBuildDirectory()), assetBundle.entries);
|
||||
|
||||
final String targetBaseName = globals.fs.path
|
||||
.withoutExtension(target).replaceFirst('lib${globals.fs.path.separator}', '');
|
||||
final Map<String, String> mappedUrls = <String, String>{
|
||||
'main.dart.js': 'packages/${flutterProject.manifest.appName}/'
|
||||
'${targetBaseName}_web_entrypoint.dart.js',
|
||||
'${targetBaseName}_web_entrypoint.dart.js.map': 'packages/${flutterProject.manifest.appName}/'
|
||||
'${targetBaseName}_web_entrypoint.dart.js.map',
|
||||
'${targetBaseName}_web_entrypoint.dart.bootstrap.js': 'packages/${flutterProject.manifest.appName}/'
|
||||
'${targetBaseName}_web_entrypoint.dart.bootstrap.js',
|
||||
'${targetBaseName}_web_entrypoint.digests': 'packages/${flutterProject.manifest.appName}/'
|
||||
'${targetBaseName}_web_entrypoint.digests',
|
||||
};
|
||||
|
||||
// Initialize the dwds server.
|
||||
final String effectiveHostname = hostname ?? _kHostName;
|
||||
final int hostPort = port == null ? await globals.os.findFreePort() : int.tryParse(port);
|
||||
|
||||
final Pipeline pipeline = const Pipeline().addMiddleware((Handler innerHandler) {
|
||||
return (Request request) async {
|
||||
// Redirect the main.dart.js to the target file we decided to serve.
|
||||
if (mappedUrls.containsKey(request.url.path)) {
|
||||
final String newPath = mappedUrls[request.url.path];
|
||||
return innerHandler(
|
||||
Request(
|
||||
request.method,
|
||||
Uri.parse(request.requestedUri.toString()
|
||||
.replaceFirst(request.requestedUri.path, '/$newPath')),
|
||||
headers: request.headers,
|
||||
url: Uri.parse(request.url.toString()
|
||||
.replaceFirst(request.url.path, newPath)),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return innerHandler(request);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Handler handler;
|
||||
Dwds dwds;
|
||||
BuildDaemonClient client;
|
||||
StreamSubscription<void> firstBuild;
|
||||
if (buildInfo.isDebug) {
|
||||
final bool hasWebPlugins = findPlugins(flutterProject)
|
||||
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
|
||||
// Start the build daemon and run an initial build.
|
||||
client = await buildDaemonCreator
|
||||
.startBuildDaemon(globals.fs.currentDirectory.path,
|
||||
release: buildInfo.isRelease,
|
||||
profile: buildInfo.isProfile,
|
||||
hasPlugins: hasWebPlugins,
|
||||
initializePlatform: initializePlatform,
|
||||
);
|
||||
client.startBuild();
|
||||
// Only provide relevant build results
|
||||
final Stream<BuildResult> filteredBuildResults = client.buildResults
|
||||
.asyncMap<BuildResult>((BuildResults results) {
|
||||
return results.results
|
||||
.firstWhere((BuildResult result) => result.target == kBuildTargetName);
|
||||
});
|
||||
// Start the build daemon and run an initial build.
|
||||
firstBuild = client.buildResults.listen((BuildResults buildResults) {
|
||||
if (firstBuildCompleter.isCompleted) {
|
||||
return;
|
||||
}
|
||||
final BuildResult result = buildResults.results.firstWhere((BuildResult result) {
|
||||
return result.target == kBuildTargetName;
|
||||
});
|
||||
if (result.status == BuildStatus.failed) {
|
||||
firstBuildCompleter.complete(false);
|
||||
}
|
||||
if (result.status == BuildStatus.succeeded) {
|
||||
firstBuildCompleter.complete(true);
|
||||
}
|
||||
});
|
||||
final int daemonAssetPort = buildDaemonCreator.assetServerPort(globals.fs.currentDirectory);
|
||||
|
||||
// Initialize the asset bundle.
|
||||
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
|
||||
await assetBundle.build();
|
||||
await writeBundle(globals.fs.directory(getAssetBuildDirectory()), assetBundle.entries);
|
||||
if (!skipDwds) {
|
||||
final BuildRunnerAssetHandler assetHandler = BuildRunnerAssetHandler(
|
||||
daemonAssetPort,
|
||||
kBuildTargetName,
|
||||
effectiveHostname,
|
||||
hostPort);
|
||||
dwds = await dwdsFactory(
|
||||
hostname: effectiveHostname,
|
||||
assetHandler: assetHandler,
|
||||
buildResults: filteredBuildResults,
|
||||
chromeConnection: () async {
|
||||
return (await ChromeLauncher.connectedInstance).chromeConnection;
|
||||
},
|
||||
reloadConfiguration: ReloadConfiguration.none,
|
||||
serveDevTools: false,
|
||||
verbose: false,
|
||||
enableDebugExtension: true,
|
||||
urlEncoder: urlTunneller,
|
||||
logWriter: (dynamic level, String message) => globals.printTrace(message),
|
||||
);
|
||||
handler = pipeline.addHandler(dwds.handler);
|
||||
} else {
|
||||
handler = pipeline.addHandler(proxyHandler('http://localhost:$daemonAssetPort/web/'));
|
||||
}
|
||||
} else {
|
||||
await buildWeb(
|
||||
flutterProject,
|
||||
target,
|
||||
buildInfo,
|
||||
initializePlatform,
|
||||
dartDefines,
|
||||
false,
|
||||
);
|
||||
firstBuildCompleter.complete(true);
|
||||
}
|
||||
|
||||
final AssetServer assetServer = buildInfo.isDebug
|
||||
? DebugAssetServer(flutterProject, targetBaseName)
|
||||
: ReleaseAssetServer();
|
||||
Cascade cascade = Cascade();
|
||||
cascade = cascade.add(handler);
|
||||
cascade = cascade.add(assetServer.handle);
|
||||
final InternetAddress internetAddress = (await InternetAddress.lookup(effectiveHostname)).first;
|
||||
final HttpServer server = await httpMultiServerFactory(internetAddress, hostPort);
|
||||
shelf_io.serveRequests(server, cascade.handler);
|
||||
final WebFs webFS = WebFs(
|
||||
client,
|
||||
server,
|
||||
dwds,
|
||||
'http://$effectiveHostname:$hostPort',
|
||||
assetServer,
|
||||
buildInfo.isDebug,
|
||||
flutterProject,
|
||||
target,
|
||||
buildInfo,
|
||||
initializePlatform,
|
||||
dartDefines,
|
||||
);
|
||||
if (!await firstBuildCompleter.future) {
|
||||
throw const BuildException();
|
||||
}
|
||||
await firstBuild?.cancel();
|
||||
return webFS;
|
||||
}
|
||||
}
|
||||
|
||||
/// An exception thrown when build runner fails.
|
||||
///
|
||||
/// This contains no error information as it will have already been printed to
|
||||
/// the console.
|
||||
class BuildException implements Exception {
|
||||
const BuildException();
|
||||
}
|
||||
|
||||
abstract class AssetServer {
|
||||
Future<Response> handle(Request request);
|
||||
|
||||
void dispose() {}
|
||||
}
|
||||
|
||||
class ReleaseAssetServer extends AssetServer {
|
||||
// Locations where source files, assets, or source maps may be located.
|
||||
final List<Uri> _searchPaths = <Uri>[
|
||||
globals.fs.directory(getWebBuildDirectory()).uri,
|
||||
globals.fs.directory(Cache.flutterRoot).parent.uri,
|
||||
globals.fs.currentDirectory.childDirectory('lib').uri,
|
||||
];
|
||||
|
||||
@override
|
||||
Future<Response> handle(Request request) async {
|
||||
Uri fileUri;
|
||||
for (final Uri uri in _searchPaths) {
|
||||
final Uri potential = uri.resolve(request.url.path);
|
||||
if (potential == null || !globals.fs.isFileSync(potential.toFilePath())) {
|
||||
continue;
|
||||
}
|
||||
fileUri = potential;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileUri != null) {
|
||||
final File file = globals.fs.file(fileUri);
|
||||
final Uint8List bytes = file.readAsBytesSync();
|
||||
// Fallback to "application/octet-stream" on null which
|
||||
// makes no claims as to the structure of the data.
|
||||
final String mimeType = mime.lookupMimeType(file.path, headerBytes: bytes)
|
||||
?? 'application/octet-stream';
|
||||
return Response.ok(bytes, headers: <String, String>{
|
||||
'Content-Type': mimeType,
|
||||
});
|
||||
}
|
||||
if (request.url.path == '') {
|
||||
final File file = globals.fs.file(globals.fs.path.join(getWebBuildDirectory(), 'index.html'));
|
||||
return Response.ok(file.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/html',
|
||||
});
|
||||
}
|
||||
return Response.notFound('');
|
||||
}
|
||||
}
|
||||
|
||||
class DebugAssetServer extends AssetServer {
|
||||
DebugAssetServer(this.flutterProject, this.targetBaseName);
|
||||
|
||||
final FlutterProject flutterProject;
|
||||
final String targetBaseName;
|
||||
final PackageMap packageMap = PackageMap(PackageMap.globalPackagesPath);
|
||||
Directory partFiles;
|
||||
|
||||
@override
|
||||
Future<Response> handle(Request request) async {
|
||||
if (request.url.path.endsWith('.html')) {
|
||||
final Uri htmlUri = flutterProject.web.directory.uri.resolveUri(request.url);
|
||||
final File htmlFile = globals.fs.file(htmlUri);
|
||||
if (htmlFile.existsSync()) {
|
||||
return Response.ok(htmlFile.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/html',
|
||||
});
|
||||
}
|
||||
return Response.notFound('');
|
||||
} else if (request.url.path.contains('stack_trace_mapper')) {
|
||||
final File file = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath),
|
||||
'lib',
|
||||
'dev_compiler',
|
||||
'web',
|
||||
'dart_stack_trace_mapper.js',
|
||||
));
|
||||
return Response.ok(file.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/javascript',
|
||||
});
|
||||
} else if (request.url.path.endsWith('part.js')) {
|
||||
// Lazily unpack any deferred imports in release/profile mode. These are
|
||||
// placed into an archive by build_runner, and are named based on the main
|
||||
// entrypoint + a "part" suffix (Though the actual names are arbitrary).
|
||||
// To make this easier to deal with they are copied into a temp directory.
|
||||
if (partFiles == null) {
|
||||
final File dart2jsArchive = globals.fs.file(globals.fs.path.join(
|
||||
flutterProject.dartTool.path,
|
||||
'build',
|
||||
'flutter_web',
|
||||
flutterProject.manifest.appName,
|
||||
'lib',
|
||||
'${targetBaseName}_web_entrypoint.dart.js.tar.gz',
|
||||
));
|
||||
if (dart2jsArchive.existsSync()) {
|
||||
final Archive archive = TarDecoder().decodeBytes(dart2jsArchive.readAsBytesSync());
|
||||
partFiles = globals.fs.systemTempDirectory.createTempSync('flutter_tool.')
|
||||
..createSync();
|
||||
for (final ArchiveFile file in archive) {
|
||||
partFiles.childFile(file.name).writeAsBytesSync(file.content as List<int>);
|
||||
}
|
||||
}
|
||||
}
|
||||
final String fileName = globals.fs.path.basename(request.url.path);
|
||||
return Response.ok(partFiles.childFile(fileName).readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/javascript',
|
||||
});
|
||||
} else if (request.url.path.contains('require.js')) {
|
||||
final File file = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath),
|
||||
'lib',
|
||||
'dev_compiler',
|
||||
'kernel',
|
||||
'amd',
|
||||
'require.js',
|
||||
));
|
||||
return Response.ok(file.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/javascript',
|
||||
});
|
||||
} else if (request.url.path.endsWith('dart_sdk.js.map')) {
|
||||
final File file = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd',
|
||||
'dart_sdk.js.map',
|
||||
));
|
||||
return Response.ok(file.readAsBytesSync());
|
||||
} else if (request.url.path.endsWith('dart_sdk.js')) {
|
||||
final File file = globals.fs.file(globals.fs.path.join(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
|
||||
'kernel',
|
||||
'amd',
|
||||
'dart_sdk.js',
|
||||
));
|
||||
return Response.ok(file.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/javascript',
|
||||
});
|
||||
} else if (request.url.path.endsWith('.dart')) {
|
||||
// This is likely a sourcemap request. The first segment is the
|
||||
// package name, and the rest is the path to the file relative to
|
||||
// the package uri. For example, `foo/bar.dart` would represent a
|
||||
// file at a path like `foo/lib/bar.dart`. If there is no leading
|
||||
// segment, then we assume it is from the current package.
|
||||
String basePath = request.url.path;
|
||||
basePath = basePath.replaceFirst('packages/build_web_compilers/', '');
|
||||
basePath = basePath.replaceFirst('packages/', '');
|
||||
|
||||
// Handle sdk requests that have mangled urls from engine build.
|
||||
if (request.url.path.contains('dart-sdk')) {
|
||||
// Note: the request is a uri and not a file path, so they always use `/`.
|
||||
final String sdkPath = globals.fs.path.joinAll(request.url.path.split('dart-sdk/').last.split('/'));
|
||||
final String dartSdkPath = globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath);
|
||||
final File candidateFile = globals.fs.file(globals.fs.path.join(dartSdkPath, sdkPath));
|
||||
return Response.ok(candidateFile.readAsBytesSync());
|
||||
}
|
||||
|
||||
// See if it is a flutter sdk path.
|
||||
final String webSdkPath = globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
|
||||
final File candidateFile = globals.fs.file(globals.fs.path.join(webSdkPath,
|
||||
basePath.split('/').join(globals.platform.pathSeparator)));
|
||||
if (candidateFile.existsSync()) {
|
||||
return Response.ok(candidateFile.readAsBytesSync());
|
||||
}
|
||||
|
||||
final String packageName = request.url.pathSegments.length == 1
|
||||
? flutterProject.manifest.appName
|
||||
: request.url.pathSegments.first;
|
||||
String filePath = globals.fs.path.joinAll(request.url.pathSegments.length == 1
|
||||
? request.url.pathSegments
|
||||
: request.url.pathSegments.skip(1));
|
||||
String packagePath = packageMap.map[packageName]?.toFilePath(windows: globals.platform.isWindows);
|
||||
// If the package isn't found, then we have an issue with relative
|
||||
// paths within the main project.
|
||||
if (packagePath == null) {
|
||||
packagePath = packageMap.map[flutterProject.manifest.appName]
|
||||
.toFilePath(windows: globals.platform.isWindows);
|
||||
filePath = request.url.path;
|
||||
}
|
||||
final File file = globals.fs.file(globals.fs.path.join(packagePath, filePath));
|
||||
if (file.existsSync()) {
|
||||
return Response.ok(file.readAsBytesSync());
|
||||
}
|
||||
return Response.notFound('');
|
||||
} else if (request.url.path.contains('assets')) {
|
||||
final String assetPath = request.url.path.replaceFirst('assets/', '');
|
||||
final File file = globals.fs.file(globals.fs.path.join(getAssetBuildDirectory(), assetPath));
|
||||
if (file.existsSync()) {
|
||||
final Uint8List bytes = file.readAsBytesSync();
|
||||
// Fallback to "application/octet-stream" on null which
|
||||
// makes no claims as to the structure of the data.
|
||||
final String mimeType = mime.lookupMimeType(file.path, headerBytes: bytes)
|
||||
?? 'application/octet-stream';
|
||||
return Response.ok(bytes, headers: <String, String>{
|
||||
'Content-Type': mimeType,
|
||||
});
|
||||
} else {
|
||||
return Response.notFound('');
|
||||
}
|
||||
}
|
||||
return Response.notFound('');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
partFiles?.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionResult {
|
||||
ConnectionResult(this.appConnection, this.debugConnection);
|
||||
|
||||
final AppConnection appConnection;
|
||||
final DebugConnection debugConnection;
|
||||
}
|
||||
|
||||
class WebTestTargetManifest {
|
||||
WebTestTargetManifest(this.buildFilters);
|
||||
|
||||
WebTestTargetManifest.all() : buildFilters = null;
|
||||
|
||||
final List<String> buildFilters;
|
||||
|
||||
bool get hasBuildFilters => buildFilters != null && buildFilters.isNotEmpty;
|
||||
}
|
||||
|
||||
/// A testable interface for starting a build daemon.
|
||||
class BuildDaemonCreator {
|
||||
const BuildDaemonCreator();
|
||||
|
||||
// TODO(jonahwilliams): find a way to get build checks working for flutter for web.
|
||||
static const String _ignoredLine1 = 'Warning: Interpreting this as package URI';
|
||||
static const String _ignoredLine2 = 'build_script.dart was not found in the asset graph, incremental builds will not work';
|
||||
static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml';
|
||||
|
||||
/// Start a build daemon and register the web targets.
|
||||
///
|
||||
/// [initializePlatform] controls whether we should invoke [webOnlyInitializePlatform].
|
||||
Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {
|
||||
bool release = false,
|
||||
bool profile = false,
|
||||
bool hasPlugins = false,
|
||||
bool initializePlatform = true,
|
||||
WebTestTargetManifest testTargets,
|
||||
}) async {
|
||||
try {
|
||||
final BuildDaemonClient client = await _connectClient(
|
||||
workingDirectory,
|
||||
release: release,
|
||||
profile: profile,
|
||||
hasPlugins: hasPlugins,
|
||||
initializePlatform: initializePlatform,
|
||||
testTargets: testTargets,
|
||||
);
|
||||
_registerBuildTargets(client, testTargets);
|
||||
return client;
|
||||
} on OptionsSkew {
|
||||
throwToolExit(
|
||||
'Incompatible options with current running build daemon.\n\n'
|
||||
'Please stop other flutter_tool instances running in this directory '
|
||||
'before starting a new instance with these options.');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _registerBuildTargets(
|
||||
BuildDaemonClient client,
|
||||
WebTestTargetManifest testTargets,
|
||||
) {
|
||||
final OutputLocation outputLocation = OutputLocation((OutputLocationBuilder b) => b
|
||||
..output = ''
|
||||
..useSymlinks = true
|
||||
..hoist = false);
|
||||
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) => b
|
||||
..target = 'web'
|
||||
..outputLocation = outputLocation?.toBuilder()));
|
||||
if (testTargets != null) {
|
||||
client.registerBuildTarget(DefaultBuildTarget((DefaultBuildTargetBuilder b) {
|
||||
b.target = 'test';
|
||||
b.outputLocation = outputLocation?.toBuilder();
|
||||
if (testTargets.hasBuildFilters) {
|
||||
b.buildFilters.addAll(testTargets.buildFilters);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Future<BuildDaemonClient> _connectClient(
|
||||
String workingDirectory, {
|
||||
bool release,
|
||||
bool profile,
|
||||
bool hasPlugins,
|
||||
bool initializePlatform,
|
||||
WebTestTargetManifest testTargets,
|
||||
}) {
|
||||
final String flutterToolsPackages = globals.fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools', '.packages');
|
||||
final String buildScript = globals.fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools', 'lib', 'src', 'build_runner', 'build_script.dart');
|
||||
final String flutterWebSdk = globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
|
||||
|
||||
// On Windows we need to call the snapshot directly otherwise
|
||||
// the process will start in a disjoint cmd without access to
|
||||
// STDIO.
|
||||
final List<String> args = <String>[
|
||||
globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||
'--packages=$flutterToolsPackages',
|
||||
buildScript,
|
||||
'daemon',
|
||||
'--skip-build-script-check',
|
||||
'--define', 'flutter_tools:ddc=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:entrypoint=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:entrypoint=release=$release',
|
||||
'--define', 'flutter_tools:entrypoint=profile=$profile',
|
||||
'--define', 'flutter_tools:shell=flutterWebSdk=$flutterWebSdk',
|
||||
'--define', 'flutter_tools:shell=hasPlugins=$hasPlugins',
|
||||
'--define', 'flutter_tools:shell=initializePlatform=$initializePlatform',
|
||||
// The following will cause build runner to only build tests that were requested.
|
||||
if (testTargets != null && testTargets.hasBuildFilters)
|
||||
for (final String buildFilter in testTargets.buildFilters)
|
||||
'--build-filter=$buildFilter',
|
||||
];
|
||||
|
||||
return BuildDaemonClient.connect(
|
||||
workingDirectory,
|
||||
args,
|
||||
logHandler: (ServerLog serverLog) {
|
||||
switch (serverLog.level) {
|
||||
case Level.SEVERE:
|
||||
case Level.SHOUT:
|
||||
// Ignore certain non-actionable messages on startup.
|
||||
if (serverLog.message.contains(_ignoredLine1) ||
|
||||
serverLog.message.contains(_ignoredLine2) ||
|
||||
serverLog.message.contains(_ignoredLine3)) {
|
||||
return;
|
||||
}
|
||||
globals.printError(serverLog.message);
|
||||
if (serverLog.error != null) {
|
||||
globals.printError(serverLog.error);
|
||||
}
|
||||
if (serverLog.stackTrace != null) {
|
||||
globals.printTrace(serverLog.stackTrace);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (serverLog.message.contains('Skipping compiling')) {
|
||||
globals.printError(serverLog.message);
|
||||
} else {
|
||||
globals.printTrace(serverLog.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
buildMode: daemon.BuildMode.Manual,
|
||||
);
|
||||
}
|
||||
|
||||
/// Retrieve the asset server port for the current daemon.
|
||||
int assetServerPort(Directory workingDirectory) {
|
||||
final String portFilePath = globals.fs.path.join(daemon.daemonWorkspace(workingDirectory.path), '.asset_server_port');
|
||||
return int.tryParse(globals.fs.file(portFilePath).readAsStringSync());
|
||||
}
|
||||
}
|
@ -192,6 +192,9 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler {
|
||||
Future<void> shutdown() {
|
||||
return _residentCompiler.shutdown();
|
||||
}
|
||||
|
||||
@override
|
||||
void addFileSystemRoot(String root) { }
|
||||
}
|
||||
|
||||
/// The current status of a codegen build.
|
||||
|
@ -25,7 +25,7 @@ const Map<String, String> _kManuallyPinnedDependencies = <String, String>{
|
||||
'flutter_gallery_assets': '0.1.9+2', // See //examples/flutter_gallery/pubspec.yaml
|
||||
'mockito': '^4.1.0', // Prevent mockito from downgrading to 4.0.0
|
||||
'vm_service_client': '0.2.6+2', // Final version before being marked deprecated.
|
||||
'dwds': '0.8.5', // Requires updates to web_fs due to breaking changes.
|
||||
'video_player': '0.10.6', // 0.10.7 fails a gallery smoke test for toString.
|
||||
};
|
||||
|
||||
class UpdatePackagesCommand extends FlutterCommand {
|
||||
|
@ -197,7 +197,8 @@ class StdoutHandler {
|
||||
class PackageUriMapper {
|
||||
PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List<String> fileSystemRoots) {
|
||||
final Map<String, Uri> packageMap = PackageMap(globals.fs.path.absolute(packagesPath)).map;
|
||||
final String scriptUri = Uri.file(scriptPath, windows: globals.platform.isWindows).toString();
|
||||
final bool isWindowsPath = globals.platform.isWindows && !scriptPath.startsWith('org-dartlang-app');
|
||||
final String scriptUri = Uri.file(scriptPath, windows: isWindowsPath).toString();
|
||||
for (final String packageName in packageMap.keys) {
|
||||
final String prefix = packageMap[packageName].toString();
|
||||
// Only perform a multi-root mapping if there are multiple roots.
|
||||
@ -463,6 +464,11 @@ abstract class ResidentCompiler {
|
||||
List<String> dartDefines,
|
||||
}) = DefaultResidentCompiler;
|
||||
|
||||
// TODO(jonahwilliams): find a better way to configure additional file system
|
||||
// roots from the runner.
|
||||
// See: https://github.com/flutter/flutter/issues/50494
|
||||
void addFileSystemRoot(String root);
|
||||
|
||||
|
||||
/// If invoked for the first time, it compiles Dart script identified by
|
||||
/// [mainPath], [invalidatedFiles] list is ignored.
|
||||
@ -538,6 +544,11 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
final List<String> experimentalFlags;
|
||||
final List<String> dartDefines;
|
||||
|
||||
@override
|
||||
void addFileSystemRoot(String root) {
|
||||
fileSystemRoots.add(root);
|
||||
}
|
||||
|
||||
/// The path to the root of the Dart SDK used to compile.
|
||||
///
|
||||
/// This is used to resolve the [platformDill].
|
||||
|
@ -37,9 +37,6 @@ class FeatureFlags {
|
||||
/// Whether the Android embedding V2 is enabled.
|
||||
bool get isAndroidEmbeddingV2Enabled => isEnabled(flutterAndroidEmbeddingV2Feature);
|
||||
|
||||
/// Whether the web incremental compiler is enabled.
|
||||
bool get isWebIncrementalCompilerEnabled => isEnabled(flutterWebIncrementalCompiler);
|
||||
|
||||
/// Whether a particular feature is enabled for the current channel.
|
||||
///
|
||||
/// Prefer using one of the specific getters above instead of this API.
|
||||
@ -72,7 +69,6 @@ const List<Feature> allFeatures = <Feature>[
|
||||
flutterMacOSDesktopFeature,
|
||||
flutterWindowsDesktopFeature,
|
||||
flutterAndroidEmbeddingV2Feature,
|
||||
flutterWebIncrementalCompiler,
|
||||
];
|
||||
|
||||
/// The [Feature] for flutter web.
|
||||
@ -154,21 +150,6 @@ const Feature flutterAndroidEmbeddingV2Feature = Feature(
|
||||
),
|
||||
);
|
||||
|
||||
/// The [Feature] for using the incremental compiler instead of build runner.
|
||||
const Feature flutterWebIncrementalCompiler = Feature(
|
||||
name: 'Enable the incremental compiler for web builds',
|
||||
configSetting: 'enable-web-incremental-compiler',
|
||||
environmentOverride: 'WEB_INCREMENTAL_COMPILER',
|
||||
master: FeatureChannelSetting(
|
||||
available: true,
|
||||
enabledByDefault: false,
|
||||
),
|
||||
dev: FeatureChannelSetting(
|
||||
available: true,
|
||||
enabledByDefault: false,
|
||||
),
|
||||
);
|
||||
|
||||
/// A [Feature] is a process for conditionally enabling tool features.
|
||||
///
|
||||
/// All settings are optional, and if not provided will generally default to
|
||||
|
@ -52,7 +52,7 @@ class FlutterDevice {
|
||||
),
|
||||
buildMode: buildMode,
|
||||
trackWidgetCreation: trackWidgetCreation,
|
||||
fileSystemRoots: fileSystemRoots,
|
||||
fileSystemRoots: fileSystemRoots ?? <String>[],
|
||||
fileSystemScheme: fileSystemScheme,
|
||||
targetModel: targetModel,
|
||||
experimentalFlags: experimentalFlags,
|
||||
@ -79,14 +79,15 @@ class FlutterDevice {
|
||||
if (device.platformType == PlatformType.fuchsia) {
|
||||
targetModel = TargetModel.flutterRunner;
|
||||
}
|
||||
if (featureFlags.isWebIncrementalCompilerEnabled &&
|
||||
targetPlatform == TargetPlatform.web_javascript) {
|
||||
if (targetPlatform == TargetPlatform.web_javascript) {
|
||||
generator = ResidentCompiler(
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: buildMode),
|
||||
buildMode: buildMode,
|
||||
trackWidgetCreation: trackWidgetCreation,
|
||||
fileSystemRoots: fileSystemRoots,
|
||||
fileSystemScheme: fileSystemScheme,
|
||||
fileSystemRoots: fileSystemRoots ?? <String>[],
|
||||
// Override the filesystem scheme so that the frontend_server can find
|
||||
// the generated entrypoint code.
|
||||
fileSystemScheme: 'org-dartlang-app',
|
||||
targetModel: TargetModel.dartdevc,
|
||||
experimentalFlags: experimentalFlags,
|
||||
platformDill: globals.fs.file(globals.artifacts
|
||||
|
@ -54,7 +54,7 @@ class CoverageCollector extends TestWatcher {
|
||||
assert(data != null);
|
||||
|
||||
print('($observatoryUri): collected coverage data; merging...');
|
||||
_addHitmap(coverage.createHitmap(data['coverage'] as List<dynamic>));
|
||||
_addHitmap(coverage.createHitmap(data['coverage'] as List<Map<String, dynamic>>));
|
||||
print('($observatoryUri): done merging coverage data into global coverage map.');
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ class CoverageCollector extends TestWatcher {
|
||||
assert(data != null);
|
||||
|
||||
globals.printTrace('pid $pid ($observatoryUri): collected coverage data; merging...');
|
||||
_addHitmap(coverage.createHitmap(data['coverage'] as List<dynamic>));
|
||||
_addHitmap(coverage.createHitmap(data['coverage'] as List<Map<String, dynamic>>));
|
||||
globals.printTrace('pid $pid ($observatoryUri): done merging coverage data into global coverage map.');
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,39 @@
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
// This logic is taken directly from https://github.com/dart-lang/build/blob/master/build_web_compilers/lib/src/dev_compiler_bootstrap.dart#L272
|
||||
// It should be fairly stable, but is otherwise required to interact with the client.js script
|
||||
// vendored with DWDS.
|
||||
const String _currentDirectoryScript = r'''
|
||||
var _currentDirectory = (function () {
|
||||
var _url;
|
||||
var lines = new Error().stack.split('\n');
|
||||
function lookupUrl() {
|
||||
if (lines.length > 2) {
|
||||
var match = lines[1].match(/^\s+at (.+):\d+:\d+$/);
|
||||
// Chrome.
|
||||
if (match) return match[1];
|
||||
// Chrome nested eval case.
|
||||
match = lines[1].match(/^\s+at eval [(](.+):\d+:\d+[)]$/);
|
||||
if (match) return match[1];
|
||||
// Edge.
|
||||
match = lines[1].match(/^\s+at.+\((.+):\d+:\d+\)$/);
|
||||
if (match) return match[1];
|
||||
// Firefox.
|
||||
match = lines[0].match(/[<][@](.+):\d+:\d+$/)
|
||||
if (match) return match[1];
|
||||
}
|
||||
// Safari.
|
||||
return lines[0].match(/(.+):\d+:\d+$/)[1];
|
||||
}
|
||||
_url = lookupUrl();
|
||||
var lastSlash = _url.lastIndexOf('/');
|
||||
if (lastSlash == -1) return _url;
|
||||
var currentDirectory = _url.substring(0, lastSlash + 1);
|
||||
return currentDirectory;
|
||||
})();
|
||||
''';
|
||||
|
||||
/// The JavaScript bootstrap script to support in-browser hot restart.
|
||||
///
|
||||
/// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl]
|
||||
@ -34,7 +67,7 @@ requireEl.defer = true;
|
||||
requireEl.async = false;
|
||||
requireEl.src = "$requireUrl";
|
||||
// This attribute tells require JS what to load as main (defined below).
|
||||
requireEl.setAttribute("data-main", "main_module");
|
||||
requireEl.setAttribute("data-main", "main_module.bootstrap");
|
||||
document.head.appendChild(requireEl);
|
||||
|
||||
// Invoked by connected chrome debugger for hot reload/restart support.
|
||||
@ -55,8 +88,8 @@ window.\$hotReloadHook = function(modules) {
|
||||
// once we've reloaded every module, trigger the hot reload.
|
||||
if (reloadCount == modules.length) {
|
||||
require(["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
|
||||
// See L81 below for an explanation.
|
||||
window.\$mainEntrypoint = app[Object.keys(app)[0]].main;
|
||||
// See the doc comment under in generateMainModule.
|
||||
window.\$dartRunMain = app[Object.keys(app)[0]].main;
|
||||
window.\$hotReload(resolve);
|
||||
});
|
||||
}
|
||||
@ -69,33 +102,108 @@ window.\$hotReloadHook = function(modules) {
|
||||
|
||||
/// Generate a synthetic main module which captures the application's main
|
||||
/// method.
|
||||
///
|
||||
/// RE: Object.keys usage in app.main:
|
||||
/// This attaches the main entrypoint and hot reload functionality to the window.
|
||||
/// The app module will have a single property which contains the actual application
|
||||
/// code. The property name is based off of the entrypoint that is generated, for example
|
||||
/// the file `foo/bar/baz.dart` will generate a property named approximately
|
||||
/// `foo__bar__baz`. Rather than attempt to guess, we assume the first property of
|
||||
/// this object is the module.
|
||||
String generateMainModule({@required String entrypoint}) {
|
||||
return '''
|
||||
return '''/* ENTRYPOINT_EXTENTION_MARKER */
|
||||
// baseUrlScript
|
||||
var baseUrl = (function () {
|
||||
// Attempt to detect --precompiled mode for tests, and set the base url
|
||||
// appropriately, otherwise set it to '/'.
|
||||
var pathParts = location.pathname.split("/");
|
||||
if (pathParts[0] == "") {
|
||||
pathParts.shift();
|
||||
}
|
||||
if (pathParts.length > 1 && pathParts[1] == "test") {
|
||||
return "/" + pathParts.slice(0, 2).join("/") + "/";
|
||||
}
|
||||
// Attempt to detect base url using <base href> html tag
|
||||
// base href should start and end with "/"
|
||||
if (typeof document !== 'undefined') {
|
||||
var el = document.getElementsByTagName('base');
|
||||
if (el && el[0] && el[0].getAttribute("href") && el[0].getAttribute
|
||||
("href").startsWith("/") && el[0].getAttribute("href").endsWith("/")){
|
||||
return el[0].getAttribute("href");
|
||||
}
|
||||
}
|
||||
// return default value
|
||||
return "/";
|
||||
}());
|
||||
$_currentDirectoryScript
|
||||
// dart loader
|
||||
if(!window.\$dartLoader) {
|
||||
window.\$dartLoader = {
|
||||
appDigests: _currentDirectory + 'basic.digests',
|
||||
moduleIdToUrl: new Map(),
|
||||
urlToModuleId: new Map(),
|
||||
rootDirectories: new Array(),
|
||||
// Used in package:build_runner/src/server/build_updates_client/hot_reload_client.dart
|
||||
moduleParentsGraph: new Map(),
|
||||
moduleLoadingErrorCallbacks: new Map(),
|
||||
forceLoadModule: function (moduleName, callback, onError) {
|
||||
if (typeof onError != 'undefined') {
|
||||
var errorCallbacks = \$dartLoader.moduleLoadingErrorCallbacks;
|
||||
if (!errorCallbacks.has(moduleName)) {
|
||||
errorCallbacks.set(moduleName, new Set());
|
||||
}
|
||||
errorCallbacks.get(moduleName).add(onError);
|
||||
}
|
||||
requirejs.undef(moduleName);
|
||||
requirejs([moduleName], function() {
|
||||
if (typeof onError != 'undefined') {
|
||||
errorCallbacks.get(moduleName).delete(onError);
|
||||
}
|
||||
if (typeof callback != 'undefined') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
getModuleLibraries: null, // set up by _initializeTools
|
||||
};
|
||||
}
|
||||
let modulePaths = {};
|
||||
let customModulePaths = {};
|
||||
window.\$dartLoader.rootDirectories.push(window.location.origin + baseUrl);
|
||||
for (let moduleName of Object.getOwnPropertyNames(modulePaths)) {
|
||||
let modulePath = modulePaths[moduleName];
|
||||
if (modulePath != moduleName) {
|
||||
customModulePaths[moduleName] = modulePath;
|
||||
}
|
||||
var src = window.location.origin + '/' + modulePath + '.js';
|
||||
if (window.\$dartLoader.moduleIdToUrl.has(moduleName)) {
|
||||
continue;
|
||||
}
|
||||
\$dartLoader.moduleIdToUrl.set(moduleName, src);
|
||||
\$dartLoader.urlToModuleId.set(src, moduleName);
|
||||
}
|
||||
// Create the main module loaded below.
|
||||
define("main_module", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
|
||||
define("main_module.bootstrap", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
|
||||
dart_sdk.dart.setStartAsyncSynchronously(true);
|
||||
dart_sdk._isolate_helper.startRootIsolate(() => {}, []);
|
||||
dart_sdk._debugger.registerDevtoolsFormatter();
|
||||
let voidToNull = () => (voidToNull = dart_sdk.dart.constFn(dart_sdk.dart.fnType(dart_sdk.core.Null, [dart_sdk.dart.void])))();
|
||||
|
||||
// Attach the main entrypoint and hot reload functionality to the window.
|
||||
// The app module will have a single property which contains the actual application
|
||||
// code. The property name is based off of the entrypoint that is generated, for example
|
||||
// the file `foo/bar/baz.dart` will generate a property named approximately
|
||||
// `foo__bar__baz`. Rather than attempt to guess, we assume the first property of
|
||||
// this object is the module.
|
||||
window.\$mainEntrypoint = app[Object.keys(app)[0]].main;
|
||||
// See the generateMainModule doc comment.
|
||||
var child = {};
|
||||
child.main = app[Object.keys(app)[0]].main;
|
||||
if (window.\$hotReload == null) {
|
||||
window.\$hotReload = function(cb) {
|
||||
dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}").then((_) => {
|
||||
dart_sdk.dart.hotRestart();
|
||||
window.\$mainEntrypoint();
|
||||
window.\$dartRunMain();
|
||||
window.requestAnimationFrame(cb);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.\$mainEntrypoint();
|
||||
/* MAIN_EXTENSION_MARKER */
|
||||
child.main();
|
||||
});
|
||||
|
||||
// Require JS configuration.
|
||||
|
@ -2,39 +2,42 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dwds/data/build_result.dart';
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mime/mime.dart' as mime;
|
||||
import 'package:package_config/discovery.dart';
|
||||
import 'package:package_config/packages.dart';
|
||||
import 'package:shelf/shelf.dart' as shelf;
|
||||
import 'package:shelf/shelf_io.dart' as shelf;
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../asset.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/net.dart';
|
||||
import '../base/utils.dart';
|
||||
import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
import '../cache.dart';
|
||||
import '../compile.dart';
|
||||
import '../convert.dart';
|
||||
import '../devfs.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import 'bootstrap.dart';
|
||||
import 'chrome.dart';
|
||||
|
||||
/// A web server which handles serving JavaScript and assets.
|
||||
///
|
||||
/// This is only used in development mode.
|
||||
class WebAssetServer {
|
||||
class WebAssetServer implements AssetReader {
|
||||
@visibleForTesting
|
||||
WebAssetServer(this._httpServer, this._packages, this.internetAddress,
|
||||
{@required void Function(dynamic, StackTrace) onError}) {
|
||||
_httpServer.listen((HttpRequest request) {
|
||||
_handleRequest(request).catchError(onError);
|
||||
// TODO(jonahwilliams): test the onError callback when https://github.com/dart-lang/sdk/issues/39094 is fixed.
|
||||
}, onError: onError);
|
||||
}
|
||||
WebAssetServer(this._httpServer, this._packages, this.internetAddress);
|
||||
|
||||
// Fallback to "application/octet-stream" on null which
|
||||
// makes no claims as to the structure of the data.
|
||||
@ -44,18 +47,49 @@ class WebAssetServer {
|
||||
///
|
||||
/// Unhandled exceptions will throw a [ToolExit] with the error and stack
|
||||
/// trace.
|
||||
static Future<WebAssetServer> start(String hostname, int port) async {
|
||||
static Future<WebAssetServer> start(
|
||||
String hostname,
|
||||
int port,
|
||||
UrlTunneller urlTunneller,
|
||||
BuildMode buildMode,
|
||||
bool enableDwds,
|
||||
) async {
|
||||
try {
|
||||
final InternetAddress address = (await InternetAddress.lookup(hostname)).first;
|
||||
final HttpServer httpServer = await HttpServer.bind(address, port);
|
||||
final Packages packages =
|
||||
await loadPackagesFile(Uri.base.resolve('.packages'), loader: (Uri uri) => globals.fs.file(uri).readAsBytes());
|
||||
return WebAssetServer(httpServer, packages, address,
|
||||
onError: (dynamic error, StackTrace stackTrace) {
|
||||
httpServer.close(force: true);
|
||||
throwToolExit(
|
||||
'Unhandled exception in web development server:\n$error\n$stackTrace');
|
||||
});
|
||||
final Packages packages = await loadPackagesFile(
|
||||
Uri.base.resolve('.packages'), loader: (Uri uri) => globals.fs.file(uri).readAsBytes());
|
||||
final WebAssetServer server = WebAssetServer(httpServer, packages, address);
|
||||
|
||||
// In release builds deploy a simpler proxy server.
|
||||
if (buildMode != BuildMode.debug) {
|
||||
final ReleaseAssetServer releaseAssetServer = ReleaseAssetServer();
|
||||
shelf.serveRequests(httpServer, releaseAssetServer.handle);
|
||||
return server;
|
||||
}
|
||||
// In debug builds, spin up DWDS and the full asset server.
|
||||
final Dwds dwds = await Dwds.start(
|
||||
assetReader: server,
|
||||
buildResults: const Stream<BuildResult>.empty(),
|
||||
chromeConnection: () async {
|
||||
final Chrome chrome = await ChromeLauncher.connectedInstance;
|
||||
return chrome.chromeConnection;
|
||||
},
|
||||
urlEncoder: urlTunneller,
|
||||
enableDebugging: true,
|
||||
logWriter: (Level logLevel, String message) => globals.printTrace(message)
|
||||
);
|
||||
shelf.Pipeline pipeline = const shelf.Pipeline();
|
||||
if (enableDwds) {
|
||||
pipeline = pipeline.addMiddleware(dwds.middleware);
|
||||
}
|
||||
final shelf.Handler dwdsHandler = pipeline.addHandler(server.handleRequest);
|
||||
final shelf.Cascade cascade = shelf.Cascade()
|
||||
.add(dwds.handler)
|
||||
.add(dwdsHandler);
|
||||
shelf.serveRequests(httpServer, cascade.handler);
|
||||
server.dwds = dwds;
|
||||
return server;
|
||||
} on SocketException catch (err) {
|
||||
throwToolExit('Failed to bind web development server:\n$err');
|
||||
}
|
||||
@ -68,10 +102,9 @@ class WebAssetServer {
|
||||
// RandomAccessFile and read on demand.
|
||||
final Map<String, Uint8List> _files = <String, Uint8List>{};
|
||||
final Map<String, Uint8List> _sourcemaps = <String, Uint8List>{};
|
||||
final RegExp _drivePath = RegExp(r'\/[A-Z]:\/');
|
||||
|
||||
final Packages _packages;
|
||||
final InternetAddress internetAddress;
|
||||
/* late final */ Dwds dwds;
|
||||
|
||||
@visibleForTesting
|
||||
Uint8List getFile(String path) => _files[path];
|
||||
@ -80,97 +113,58 @@ class WebAssetServer {
|
||||
Uint8List getSourceMap(String path) => _sourcemaps[path];
|
||||
|
||||
// handle requests for JavaScript source, dart sources maps, or asset files.
|
||||
Future<void> _handleRequest(HttpRequest request) async {
|
||||
final HttpResponse response = request.response;
|
||||
@visibleForTesting
|
||||
Future<shelf.Response> handleRequest(shelf.Request request) async {
|
||||
final Map<String, String> headers = <String, String>{};
|
||||
// If the response is `/`, then we are requesting the index file.
|
||||
if (request.uri.path == '/') {
|
||||
if (request.url.path == '/' || request.url.path.isEmpty) {
|
||||
final File indexFile = globals.fs.currentDirectory
|
||||
.childDirectory('web')
|
||||
.childFile('index.html');
|
||||
.childDirectory('web')
|
||||
.childFile('index.html');
|
||||
if (indexFile.existsSync()) {
|
||||
response.headers.add('Content-Type', 'text/html');
|
||||
response.headers.add('Content-Length', indexFile.lengthSync());
|
||||
await response.addStream(indexFile.openRead());
|
||||
} else {
|
||||
response.statusCode = HttpStatus.notFound;
|
||||
headers[HttpHeaders.contentTypeHeader] = 'text/html';
|
||||
headers[HttpHeaders.contentLengthHeader] = indexFile.lengthSync().toString();
|
||||
return shelf.Response.ok(indexFile.openRead(), headers: headers);
|
||||
}
|
||||
await response.close();
|
||||
return;
|
||||
return shelf.Response.notFound('');
|
||||
}
|
||||
|
||||
// TODO(jonahwilliams): better path normalization in frontend_server to remove
|
||||
// this workaround.
|
||||
String requestPath = request.uri.path;
|
||||
if (requestPath.startsWith(_drivePath)) {
|
||||
requestPath = requestPath.substring(3);
|
||||
}
|
||||
// NOTE: shelf removes leading `/` for some reason.
|
||||
final String requestPath = request.url.path.startsWith('/')
|
||||
? request.url.path
|
||||
: '/${request.url.path}';
|
||||
|
||||
// If this is a JavaScript file, it must be in the in-memory cache.
|
||||
// Attempt to look up the file by URI.
|
||||
if (_files.containsKey(requestPath)) {
|
||||
final List<int> bytes = getFile(requestPath);
|
||||
response.headers
|
||||
..add('Content-Length', bytes.length)
|
||||
..add('Content-Type', 'application/javascript');
|
||||
response.add(bytes);
|
||||
await response.close();
|
||||
return;
|
||||
headers[HttpHeaders.contentLengthHeader] = bytes.length.toString();
|
||||
headers[HttpHeaders.contentTypeHeader] = 'application/javascript';
|
||||
return shelf.Response.ok(bytes, headers: headers);
|
||||
}
|
||||
// If this is a sourcemap file, then it might be in the in-memory cache.
|
||||
// Attempt to lookup the file by URI.
|
||||
if (_sourcemaps.containsKey(requestPath)) {
|
||||
final List<int> bytes = getSourceMap(requestPath);
|
||||
response.headers
|
||||
..add('Content-Length', bytes.length)
|
||||
..add('Content-Type', 'application/json');
|
||||
response.add(bytes);
|
||||
await response.close();
|
||||
return;
|
||||
headers[HttpHeaders.contentLengthHeader] = bytes.length.toString();
|
||||
headers[HttpHeaders.contentTypeHeader] = 'application/json';
|
||||
return shelf.Response.ok(bytes, headers: headers);
|
||||
}
|
||||
|
||||
// If this is a dart file, it must be on the local file system and is
|
||||
// likely coming from a source map request. Attempt to look in the
|
||||
// local filesystem for it, and return a 404 if it is not found. The tool
|
||||
// doesn't currently consider the case of Dart files as assets.
|
||||
File file = globals.fs.file(Uri.base.resolve(request.uri.path));
|
||||
|
||||
// If both of the lookups above failed, the file might have been a package
|
||||
// file which is signaled by a `/packages/<package>/<path>` request.
|
||||
if (!file.existsSync() && request.uri.pathSegments.first == 'packages') {
|
||||
file = globals.fs.file(_packages.resolve(Uri(
|
||||
scheme: 'package', pathSegments: request.uri.pathSegments.skip(1))));
|
||||
}
|
||||
File file = _resolveDartFile(requestPath);
|
||||
|
||||
// If all of the lookups above failed, the file might have been an asset.
|
||||
// Try and resolve the path relative to the built asset directory.
|
||||
if (!file.existsSync()) {
|
||||
final String assetPath = request.uri.path.replaceFirst('/assets/', '');
|
||||
file = globals.fs.file(globals.fs.path
|
||||
.join(getAssetBuildDirectory(), globals.fs.path.relative(assetPath)));
|
||||
}
|
||||
|
||||
// If it isn't a project source or an asset, it must be a dart SDK source.
|
||||
// or a flutter web SDK source.
|
||||
if (!file.existsSync()) {
|
||||
final Directory dartSdkParent = globals.fs
|
||||
.directory(
|
||||
globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath))
|
||||
.parent;
|
||||
file = globals.fs.file(globals.fs.path
|
||||
.joinAll(<String>[dartSdkParent.path, ...request.uri.pathSegments]));
|
||||
final String assetPath = requestPath.replaceFirst('/assets/', '');
|
||||
file = globals.fs.file(
|
||||
globals.fs.path.join(getAssetBuildDirectory(),
|
||||
globals.fs.path.relative(assetPath)),
|
||||
);
|
||||
}
|
||||
|
||||
if (!file.existsSync()) {
|
||||
final String flutterWebSdk =
|
||||
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
|
||||
file = globals.fs.file(globals.fs.path
|
||||
.joinAll(<String>[flutterWebSdk, ...request.uri.pathSegments]));
|
||||
}
|
||||
|
||||
if (!file.existsSync()) {
|
||||
response.statusCode = HttpStatus.notFound;
|
||||
await response.close();
|
||||
return;
|
||||
return shelf.Response.notFound('');
|
||||
}
|
||||
final int length = file.lengthSync();
|
||||
// Attempt to determine the file's mime type. if this is not provided some
|
||||
@ -184,10 +178,9 @@ class WebAssetServer {
|
||||
);
|
||||
}
|
||||
mimeType ??= _kDefaultMimeType;
|
||||
response.headers.add('Content-Length', length);
|
||||
response.headers.add('Content-Type', mimeType);
|
||||
await response.addStream(file.openRead());
|
||||
await response.close();
|
||||
headers[HttpHeaders.contentLengthHeader] = length.toString();
|
||||
headers[HttpHeaders.contentTypeHeader] = mimeType;
|
||||
return shelf.Response.ok(file.openRead(), headers: headers);
|
||||
}
|
||||
|
||||
/// Tear down the http server running.
|
||||
@ -207,19 +200,15 @@ class WebAssetServer {
|
||||
final List<String> modules = <String>[];
|
||||
final Uint8List codeBytes = codeFile.readAsBytesSync();
|
||||
final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync();
|
||||
final Map<String, dynamic> manifest =
|
||||
castStringKeyedMap(json.decode(manifestFile.readAsStringSync()));
|
||||
final Map<String, dynamic> manifest = castStringKeyedMap(json.decode(manifestFile.readAsStringSync()));
|
||||
for (final String filePath in manifest.keys) {
|
||||
if (filePath == null) {
|
||||
globals.printTrace('Invalid manfiest file: $filePath');
|
||||
continue;
|
||||
}
|
||||
final Map<String, dynamic> offsets =
|
||||
castStringKeyedMap(manifest[filePath]);
|
||||
final List<int> codeOffsets =
|
||||
(offsets['code'] as List<dynamic>).cast<int>();
|
||||
final List<int> sourcemapOffsets =
|
||||
(offsets['sourcemap'] as List<dynamic>).cast<int>();
|
||||
final Map<String, dynamic> offsets = castStringKeyedMap(manifest[filePath]);
|
||||
final List<int> codeOffsets = (offsets['code'] as List<dynamic>).cast<int>();
|
||||
final List<int> sourcemapOffsets = (offsets['sourcemap'] as List<dynamic>).cast<int>();
|
||||
if (codeOffsets.length != 2 || sourcemapOffsets.length != 2) {
|
||||
globals.printTrace('Invalid manifest byte offsets: $offsets');
|
||||
continue;
|
||||
@ -236,13 +225,12 @@ class WebAssetServer {
|
||||
codeStart,
|
||||
codeEnd - codeStart,
|
||||
);
|
||||
_files[_filePathToUriFragment(filePath)] = byteView;
|
||||
_files[filePath] = byteView;
|
||||
|
||||
final int sourcemapStart = sourcemapOffsets[0];
|
||||
final int sourcemapEnd = sourcemapOffsets[1];
|
||||
if (sourcemapStart < 0 || sourcemapEnd > sourcemapBytes.lengthInBytes) {
|
||||
globals
|
||||
.printTrace('Invalid byte index: [$sourcemapStart, $sourcemapEnd]');
|
||||
globals.printTrace('Invalid byte index: [$sourcemapStart, $sourcemapEnd]');
|
||||
continue;
|
||||
}
|
||||
final Uint8List sourcemapView = Uint8List.view(
|
||||
@ -250,24 +238,132 @@ class WebAssetServer {
|
||||
sourcemapStart,
|
||||
sourcemapEnd - sourcemapStart,
|
||||
);
|
||||
_sourcemaps['${_filePathToUriFragment(filePath)}.map'] = sourcemapView;
|
||||
_sourcemaps['$filePath.map'] = sourcemapView;
|
||||
|
||||
modules.add(filePath);
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
// Attempt to resolve `path` to a dart file.
|
||||
File _resolveDartFile(String path) {
|
||||
// If this is a dart file, it must be on the local file system and is
|
||||
// likely coming from a source map request. The tool doesn't currently
|
||||
// consider the case of Dart files as assets.
|
||||
final File dartFile = globals.fs.file(globals.fs.currentDirectory.uri.resolve(path));
|
||||
if (dartFile.existsSync()) {
|
||||
return dartFile;
|
||||
}
|
||||
|
||||
final List<String> segments = path.split('/');
|
||||
if (segments.first.isEmpty) {
|
||||
segments.removeAt(0);
|
||||
}
|
||||
|
||||
// The file might have been a package file which is signaled by a
|
||||
// `/packages/<package>/<path>` request.
|
||||
if (segments.first == 'packages') {
|
||||
final File packageFile = globals.fs.file(_packages.resolve(Uri(
|
||||
scheme: 'package', pathSegments: segments.skip(1))));
|
||||
if (packageFile.existsSync()) {
|
||||
return packageFile;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise it must be a Dart SDK source or a Flutter Web SDK source.
|
||||
final Directory dartSdkParent = globals.fs
|
||||
.directory(globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath))
|
||||
.parent;
|
||||
final File dartSdkFile = globals.fs.file(globals.fs.path
|
||||
.joinAll(<String>[dartSdkParent.path, ...segments]));
|
||||
if (dartSdkFile.existsSync()) {
|
||||
return dartSdkFile;
|
||||
}
|
||||
|
||||
final String flutterWebSdk = globals.artifacts
|
||||
.getArtifactPath(Artifact.flutterWebSdk);
|
||||
final File webSdkFile = globals.fs
|
||||
.file(globals.fs.path.joinAll(<String>[flutterWebSdk, ...segments]));
|
||||
|
||||
return webSdkFile;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> dartSourceContents(String serverPath) {
|
||||
final File result = _resolveDartFile(serverPath);
|
||||
if (result.existsSync()) {
|
||||
return result.readAsString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> sourceMapContents(String serverPath) async {
|
||||
return utf8.decode(_sourcemaps[serverPath]);
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionResult {
|
||||
ConnectionResult(this.appConnection, this.debugConnection);
|
||||
|
||||
final AppConnection appConnection;
|
||||
final DebugConnection debugConnection;
|
||||
}
|
||||
|
||||
class WebDevFS implements DevFS {
|
||||
WebDevFS(this.hostname, this.port, this._packagesFilePath);
|
||||
WebDevFS({
|
||||
@required this.hostname,
|
||||
@required this.port,
|
||||
@required this.packagesFilePath,
|
||||
@required this.urlTunneller,
|
||||
@required this.buildMode,
|
||||
@required this.enableDwds,
|
||||
});
|
||||
|
||||
final String hostname;
|
||||
final int port;
|
||||
final String _packagesFilePath;
|
||||
final String packagesFilePath;
|
||||
final UrlTunneller urlTunneller;
|
||||
final BuildMode buildMode;
|
||||
final bool enableDwds;
|
||||
|
||||
@visibleForTesting
|
||||
WebAssetServer webAssetServer;
|
||||
|
||||
Dwds get dwds => webAssetServer.dwds;
|
||||
|
||||
Future<DebugConnection> _cachedExtensionFuture;
|
||||
StreamSubscription<void> _connectedApps;
|
||||
|
||||
/// Connect and retrieve the [DebugConnection] for the current application.
|
||||
///
|
||||
/// Only calls [AppConnection.runMain] on the subsequent connections.
|
||||
Future<ConnectionResult> connect(bool useDebugExtension) {
|
||||
final Completer<ConnectionResult> firstConnection = Completer<ConnectionResult>();
|
||||
_connectedApps = dwds.connectedApps.listen((AppConnection appConnection) async {
|
||||
try {
|
||||
final DebugConnection debugConnection = useDebugExtension
|
||||
? await (_cachedExtensionFuture ??= dwds.extensionDebugConnections.stream.first)
|
||||
: await dwds.debugConnection(appConnection);
|
||||
if (firstConnection.isCompleted) {
|
||||
appConnection.runMain();
|
||||
} else {
|
||||
firstConnection.complete(ConnectionResult(appConnection, debugConnection));
|
||||
}
|
||||
} on Exception catch (error, stackTrace) {
|
||||
if (!firstConnection.isCompleted) {
|
||||
firstConnection.completeError(error, stackTrace);
|
||||
}
|
||||
}
|
||||
}, onError: (dynamic error, StackTrace stackTrace) {
|
||||
globals.printError('Unknown error while waiting for debug connection:$error\n$stackTrace');
|
||||
if (!firstConnection.isCompleted) {
|
||||
firstConnection.completeError(error, stackTrace);
|
||||
}
|
||||
});
|
||||
return firstConnection.future;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Uri> sources = <Uri>[];
|
||||
|
||||
@ -283,13 +379,20 @@ class WebDevFS implements DevFS {
|
||||
|
||||
@override
|
||||
Future<Uri> create() async {
|
||||
webAssetServer = await WebAssetServer.start(hostname, port);
|
||||
webAssetServer = await WebAssetServer.start(
|
||||
hostname,
|
||||
port,
|
||||
urlTunneller,
|
||||
buildMode,
|
||||
enableDwds,
|
||||
);
|
||||
return Uri.parse('http://$hostname:$port');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> destroy() async {
|
||||
await webAssetServer.dispose();
|
||||
await _connectedApps?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -321,49 +424,60 @@ class WebDevFS implements DevFS {
|
||||
}) async {
|
||||
assert(trackWidgetCreation != null);
|
||||
assert(generator != null);
|
||||
final String outputDirectoryPath = globals.fs.file(mainPath).parent.path;
|
||||
|
||||
if (bundleFirstUpload) {
|
||||
final String entrypoint = PackageUriMapper(mainPath, '.packages', null, null)
|
||||
.map(mainPath)
|
||||
?.pathSegments?.join('/');
|
||||
generator.addFileSystemRoot(outputDirectoryPath);
|
||||
final String entrypoint = globals.fs.path.basename(mainPath);
|
||||
webAssetServer.writeFile('/manifest.json', '{"info":"manifest not generated in run mode."}');
|
||||
webAssetServer.writeFile('/flutter_service_worker.js', '// Service worker not loaded in run mode.');
|
||||
webAssetServer.writeFile(
|
||||
'/main.dart.js',
|
||||
generateBootstrapScript(
|
||||
requireUrl: _filePathToUriFragment(requireJS.path),
|
||||
mapperUrl: _filePathToUriFragment(stackTraceMapper.path),
|
||||
entrypoint: entrypoint != null ? '/packages/$entrypoint.lib.js' : '$mainPath.lib.js',
|
||||
));
|
||||
'/main.dart.js',
|
||||
generateBootstrapScript(
|
||||
requireUrl: _filePathToUriFragment(requireJS.path),
|
||||
mapperUrl: _filePathToUriFragment(stackTraceMapper.path),
|
||||
entrypoint: '/$entrypoint.lib.js',
|
||||
),
|
||||
);
|
||||
webAssetServer.writeFile(
|
||||
'/main_module.js',
|
||||
generateMainModule(
|
||||
entrypoint: entrypoint != null ? '/packages/$entrypoint.lib.js' : '$mainPath.lib.js',
|
||||
));
|
||||
'/main_module.bootstrap.js',
|
||||
generateMainModule(
|
||||
entrypoint: '/$entrypoint.lib.js',
|
||||
),
|
||||
);
|
||||
// TODO(jonahwilliams): switch to DWDS provided APIs when they are ready.
|
||||
webAssetServer.writeFile('/basic.digests', '{}');
|
||||
webAssetServer.writeFile('/dart_sdk.js', dartSdk.readAsStringSync());
|
||||
webAssetServer.writeFile(
|
||||
'/dart_sdk.js.map', dartSdkSourcemap.readAsStringSync());
|
||||
webAssetServer.writeFile('/dart_sdk.js.map', dartSdkSourcemap.readAsStringSync());
|
||||
// TODO(jonahwilliams): refactor the asset code in this and the regular devfs to
|
||||
// be shared.
|
||||
if (bundle != null) {
|
||||
await writeBundle(
|
||||
globals.fs.directory(getAssetBuildDirectory()), bundle.entries);
|
||||
globals.fs.directory(getAssetBuildDirectory()),
|
||||
bundle.entries,
|
||||
);
|
||||
}
|
||||
}
|
||||
final DateTime candidateCompileTime = DateTime.now();
|
||||
if (fullRestart) {
|
||||
generator.reset();
|
||||
}
|
||||
|
||||
// The tool generates an entrypoint file in a temp directory to handle
|
||||
// the web specific bootrstrap logic. To make it easier for DWDS to handle
|
||||
// mapping the file name, this is done via an additional file root and
|
||||
// specicial hard-coded scheme.
|
||||
final CompilerOutput compilerOutput = await generator.recompile(
|
||||
mainPath,
|
||||
'org-dartlang-app:///' + globals.fs.path.basename(mainPath),
|
||||
invalidatedFiles,
|
||||
outputPath: dillOutputPath ??
|
||||
getDefaultApplicationKernelPath(
|
||||
trackWidgetCreation: trackWidgetCreation),
|
||||
packagesFilePath: _packagesFilePath,
|
||||
getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation),
|
||||
packagesFilePath: packagesFilePath,
|
||||
);
|
||||
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
||||
return UpdateFSReport(success: false);
|
||||
}
|
||||
|
||||
// Only update the last compiled time if we successfully compiled.
|
||||
lastCompiled = candidateCompileTime;
|
||||
// list of sources that needs to be monitored are in [compilerOutput.sources]
|
||||
@ -373,18 +487,19 @@ class WebDevFS implements DevFS {
|
||||
File sourcemapFile;
|
||||
List<String> modules;
|
||||
try {
|
||||
codeFile = globals.fs.file('${compilerOutput.outputFilename}.sources');
|
||||
manifestFile = globals.fs.file('${compilerOutput.outputFilename}.json');
|
||||
sourcemapFile = globals.fs.file('${compilerOutput.outputFilename}.map');
|
||||
final Directory parentDirectory = globals.fs.directory(outputDirectoryPath);
|
||||
codeFile = parentDirectory.childFile('${compilerOutput.outputFilename}.sources');
|
||||
manifestFile = parentDirectory.childFile('${compilerOutput.outputFilename}.json');
|
||||
sourcemapFile = parentDirectory.childFile('${compilerOutput.outputFilename}.map');
|
||||
modules = webAssetServer.write(codeFile, manifestFile, sourcemapFile);
|
||||
} on FileSystemException catch (err) {
|
||||
throwToolExit('Failed to load recompiled sources:\n$err');
|
||||
}
|
||||
return UpdateFSReport(
|
||||
success: true,
|
||||
syncedBytes: codeFile.lengthSync(),
|
||||
invalidatedSourcesCount: invalidatedFiles.length)
|
||||
..invalidatedModules = modules.map(_filePathToUriFragment).toList();
|
||||
success: true,
|
||||
syncedBytes: codeFile.lengthSync(),
|
||||
invalidatedSourcesCount: invalidatedFiles.length,
|
||||
)..invalidatedModules = modules;
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
@ -435,3 +550,43 @@ String _filePathToUriFragment(String path) {
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
class ReleaseAssetServer {
|
||||
// Locations where source files, assets, or source maps may be located.
|
||||
final List<Uri> _searchPaths = <Uri>[
|
||||
globals.fs.directory(getWebBuildDirectory()).uri,
|
||||
globals.fs.directory(Cache.flutterRoot).parent.uri,
|
||||
globals.fs.currentDirectory.childDirectory('lib').uri,
|
||||
];
|
||||
|
||||
Future<shelf.Response> handle(shelf.Request request) async {
|
||||
Uri fileUri;
|
||||
for (final Uri uri in _searchPaths) {
|
||||
final Uri potential = uri.resolve(request.url.path);
|
||||
if (potential == null || !globals.fs.isFileSync(potential.toFilePath())) {
|
||||
continue;
|
||||
}
|
||||
fileUri = potential;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fileUri != null) {
|
||||
final File file = globals.fs.file(fileUri);
|
||||
final Uint8List bytes = file.readAsBytesSync();
|
||||
// Fallback to "application/octet-stream" on null which
|
||||
// makes no claims as to the structure of the data.
|
||||
final String mimeType = mime.lookupMimeType(file.path, headerBytes: bytes)
|
||||
?? 'application/octet-stream';
|
||||
return shelf.Response.ok(bytes, headers: <String, String>{
|
||||
'Content-Type': mimeType,
|
||||
});
|
||||
}
|
||||
if (request.url.path == '') {
|
||||
final File file = globals.fs.file(globals.fs.path.join(getWebBuildDirectory(), 'index.html'));
|
||||
return shelf.Response.ok(file.readAsBytesSync(), headers: <String, String>{
|
||||
'Content-Type': 'text/html',
|
||||
});
|
||||
}
|
||||
return shelf.Response.notFound('');
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ dependencies:
|
||||
# To update these, use "flutter update-packages --force-upgrade".
|
||||
archive: 2.0.11
|
||||
args: 1.5.2
|
||||
dwds: 0.8.5
|
||||
dwds: 1.0.1
|
||||
completion: 0.2.1+1
|
||||
coverage: 0.13.5
|
||||
coverage: 0.13.6
|
||||
crypto: 2.1.3
|
||||
file: 5.1.0
|
||||
http: 0.12.0+4
|
||||
@ -94,6 +94,7 @@ dependencies:
|
||||
pubspec_parse: 0.1.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
scratch_space: 0.0.4+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf: 0.7.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_proxy: 0.1.0+7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_static: 0.2.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_web_socket: 0.2.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
@ -122,10 +123,9 @@ dev_dependencies:
|
||||
|
||||
multi_server_socket: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
node_preamble: 1.4.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
shelf_packages_handler: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||
|
||||
dartdoc:
|
||||
# Exclude this package from the hosted API docs.
|
||||
nodoc: true
|
||||
|
||||
# PUBSPEC CHECKSUM: 557d
|
||||
# PUBSPEC CHECKSUM: c273
|
||||
|
@ -1,78 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:build/build.dart';
|
||||
import 'package:build_runner_core/build_runner_core.dart';
|
||||
import 'package:flutter_tools/src/build_runner/web_compilation_delegate.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:glob/glob.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/io.dart';
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
group('MultirootFileBasedAssetReader', () {
|
||||
Testbed testbed;
|
||||
FakePackageGraph packageGraph;
|
||||
|
||||
setUp(() {
|
||||
testbed = Testbed(setup: () {
|
||||
final PackageNode root = PackageNode('foobar', globals.fs.currentDirectory.path, DependencyType.path);
|
||||
packageGraph = FakePackageGraph(root, <String, PackageNode>{'foobar': root});
|
||||
globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('main');
|
||||
globals.fs.file(globals.fs.path.join('.dart_tool', 'build', 'generated', 'foobar', 'lib', 'bar.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('bar');
|
||||
globals.fs.file('pubspec.yaml')
|
||||
..createSync()
|
||||
..writeAsStringSync('name: foobar');
|
||||
});
|
||||
});
|
||||
|
||||
test('Can find assets from the generated directory', () => testbed.run(() async {
|
||||
await IOOverrides.runWithIOOverrides(() async {
|
||||
final MultirootFileBasedAssetReader reader = MultirootFileBasedAssetReader(
|
||||
packageGraph,
|
||||
globals.fs.directory(globals.fs.path.join('.dart_tool', 'build', 'generated')),
|
||||
);
|
||||
expect(await reader.canRead(AssetId('foobar', 'lib/bar.dart')), true);
|
||||
expect(await reader.canRead(AssetId('foobar', 'lib/main.dart')), true);
|
||||
|
||||
expect(await reader.readAsString(AssetId('foobar', 'lib/bar.dart')), 'bar');
|
||||
expect(await reader.readAsString(AssetId('foobar', 'lib/main.dart')), 'main');
|
||||
|
||||
expect(await reader.readAsBytes(AssetId('foobar', 'lib/bar.dart')), utf8.encode('bar'));
|
||||
expect(await reader.readAsBytes(AssetId('foobar', 'lib/main.dart')), utf8.encode('main'));
|
||||
|
||||
expect(await reader.findAssets(Glob('**')).toList(), unorderedEquals(<AssetId>[
|
||||
AssetId('foobar', 'pubspec.yaml'),
|
||||
AssetId('foobar', 'lib/bar.dart'),
|
||||
AssetId('foobar', 'lib/main.dart'),
|
||||
]));
|
||||
}, FlutterIOOverrides(fileSystem: globals.fs));
|
||||
// Some component of either dart:io or build_runner normalizes file uris
|
||||
// into file paths for windows. This doesn't seem to work with IOOverrides
|
||||
// leaving all filepaths on windows with forward slashes.
|
||||
}), skip: Platform.isWindows);
|
||||
});
|
||||
}
|
||||
|
||||
class FakePackageGraph implements PackageGraph {
|
||||
FakePackageGraph(this.root, this.allPackages);
|
||||
|
||||
@override
|
||||
final Map<String, PackageNode> allPackages;
|
||||
|
||||
@override
|
||||
final PackageNode root;
|
||||
|
||||
@override
|
||||
PackageNode operator [](String packageName) => allPackages[packageName];
|
||||
}
|
@ -15,7 +15,6 @@ import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/devfs.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
@ -701,8 +700,6 @@ void main() {
|
||||
globals.fs.file(globals.artifacts.getArtifactPath(Artifact.webPlatformKernelDill, mode: BuildMode.debug))
|
||||
.absolute.uri.toString(),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('connect sets up log reader', () => testbed.run(() async {
|
||||
|
@ -8,17 +8,16 @@ import 'package:dwds/dwds.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/base/net.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/resident_runner.dart';
|
||||
import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
|
||||
import 'package:flutter_tools/src/build_runner/web_fs.dart';
|
||||
import 'package:flutter_tools/src/web/chrome.dart';
|
||||
import 'package:flutter_tools/src/web/devfs_web.dart';
|
||||
import 'package:flutter_tools/src/web/web_device.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
@ -29,15 +28,22 @@ import '../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
MockFlutterWebFs mockWebFs;
|
||||
ResidentWebRunner residentWebRunner;
|
||||
MockFlutterDevice mockFlutterDevice;
|
||||
MockWebDevFS mockWebDevFS;
|
||||
MockBuildSystem mockBuildSystem;
|
||||
|
||||
setUp(() {
|
||||
mockWebFs = MockFlutterWebFs();
|
||||
mockWebDevFS = MockWebDevFS();
|
||||
mockBuildSystem = MockBuildSystem();
|
||||
final MockWebDevice mockWebDevice = MockWebDevice();
|
||||
mockFlutterDevice = MockFlutterDevice();
|
||||
when(mockFlutterDevice.device).thenReturn(mockWebDevice);
|
||||
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
|
||||
when(mockWebDevFS.sources).thenReturn(<Uri>[]);
|
||||
when(mockBuildSystem.build(any, any)).thenAnswer((Invocation invocation) async {
|
||||
return BuildResult(success: true);
|
||||
});
|
||||
testbed = Testbed(
|
||||
setup: () {
|
||||
residentWebRunner = residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
|
||||
@ -50,29 +56,14 @@ void main() {
|
||||
urlTunneller: null,
|
||||
) as ResidentWebRunner;
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
WebFsFactory: () => ({
|
||||
@required String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@required BuildInfo buildInfo,
|
||||
@required bool skipDwds,
|
||||
@required bool initializePlatform,
|
||||
@required String hostname,
|
||||
@required String port,
|
||||
@required UrlTunneller urlTunneller,
|
||||
@required List<String> dartDefines,
|
||||
}) async {
|
||||
return mockWebFs;
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
void _setupMocks() {
|
||||
globals.fs.file('.packages').writeAsStringSync('\n');
|
||||
globals.fs.file('pubspec.yaml').createSync();
|
||||
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true);
|
||||
when(mockWebFs.connect(any)).thenThrow(StateError('debugging not supported'));
|
||||
}
|
||||
|
||||
test('Can successfully run and connect without vmservice', () => testbed.run(() async {
|
||||
@ -89,6 +80,7 @@ void main() {
|
||||
expect(debugConnectionInfo.wsUri, null);
|
||||
verify(mockStatus.stop()).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
BuildSystem: () => mockBuildSystem,
|
||||
Logger: () => DelegateLogger(BufferLogger(
|
||||
terminal: AnsiTerminal(
|
||||
stdio: null,
|
||||
@ -105,12 +97,11 @@ void main() {
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return true;
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(result.code, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
BuildSystem: () => mockBuildSystem,
|
||||
}));
|
||||
|
||||
test('Fails on compilation errors in hot restart', () => testbed.run(() async {
|
||||
@ -120,13 +111,15 @@ void main() {
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return false;
|
||||
when(mockBuildSystem.build(any, any)).thenAnswer((Invocation invocation) async {
|
||||
return BuildResult(success: false);
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(result.code, 1);
|
||||
expect(result.message, contains('Failed to recompile application.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
BuildSystem: () => mockBuildSystem,
|
||||
}));
|
||||
|
||||
test('Correctly performs a full refresh on attached chrome device.', () => testbed.run(() async {
|
||||
@ -150,22 +143,20 @@ void main() {
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return true;
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(result.code, 0);
|
||||
verify(mockWipConnection.sendCommand('Page.reload', <String, Object>{
|
||||
'ignoreCache': true,
|
||||
})).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
BuildSystem: () => mockBuildSystem,
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
class MockWebDevFS extends Mock implements WebDevFS {}
|
||||
class MockWebDevice extends Mock implements Device {}
|
||||
class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {}
|
||||
class MockFlutterWebFs extends Mock implements WebFs {}
|
||||
class MockDebugConnection extends Mock implements DebugConnection {}
|
||||
class MockVmService extends Mock implements VmService {}
|
||||
class MockStatus extends Mock implements Status {}
|
||||
@ -175,3 +166,4 @@ class MockChrome extends Mock implements Chrome {}
|
||||
class MockChromeConnection extends Mock implements ChromeConnection {}
|
||||
class MockChromeTab extends Mock implements ChromeTab {}
|
||||
class MockWipConnection extends Mock implements WipConnection {}
|
||||
class MockBuildSystem extends Mock implements BuildSystem {}
|
||||
|
@ -5,27 +5,22 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:build_daemon/client.dart';
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/net.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
|
||||
import 'package:flutter_tools/src/build_runner/web_fs.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/devfs.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/resident_runner.dart';
|
||||
import 'package:flutter_tools/src/web/chrome.dart';
|
||||
import 'package:flutter_tools/src/web/devfs_web.dart';
|
||||
import 'package:flutter_tools/src/web/web_device.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
@ -37,7 +32,6 @@ import '../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
MockFlutterWebFs mockWebFs;
|
||||
ResidentWebRunner residentWebRunner;
|
||||
MockDebugConnection mockDebugConnection;
|
||||
MockVmService mockVmService;
|
||||
@ -52,14 +46,13 @@ void main() {
|
||||
MockWipConnection mockWipConnection;
|
||||
MockWipDebugger mockWipDebugger;
|
||||
MockWebServerDevice mockWebServerDevice;
|
||||
bool didSkipDwds;
|
||||
MockDevice mockDevice;
|
||||
|
||||
setUp(() {
|
||||
resetChromeForTesting();
|
||||
mockWebFs = MockFlutterWebFs();
|
||||
mockDebugConnection = MockDebugConnection();
|
||||
mockVmService = MockVmService();
|
||||
mockChromeDevice = MockChromeDevice();
|
||||
mockDevice = MockDevice();
|
||||
mockAppConnection = MockAppConnection();
|
||||
mockFlutterDevice = MockFlutterDevice();
|
||||
mockWebDevFS = MockWebDevFS();
|
||||
@ -70,7 +63,11 @@ void main() {
|
||||
mockWipConnection = MockWipConnection();
|
||||
mockWipDebugger = MockWipDebugger();
|
||||
mockWebServerDevice = MockWebServerDevice();
|
||||
when(mockFlutterDevice.device).thenReturn(mockChromeDevice);
|
||||
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
|
||||
when(mockFlutterDevice.device).thenReturn(mockDevice);
|
||||
when(mockWebDevFS.connect(any)).thenAnswer((Invocation invocation) async {
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
testbed = Testbed(
|
||||
setup: () {
|
||||
residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
|
||||
@ -85,22 +82,6 @@ void main() {
|
||||
globals.fs.currentDirectory.childFile('.packages')
|
||||
..writeAsStringSync('\n');
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
WebFsFactory: () => ({
|
||||
@required String target,
|
||||
@required FlutterProject flutterProject,
|
||||
@required BuildInfo buildInfo,
|
||||
@required bool skipDwds,
|
||||
@required bool initializePlatform,
|
||||
@required String hostname,
|
||||
@required String port,
|
||||
@required List<String> dartDefines,
|
||||
@required UrlTunneller urlTunneller,
|
||||
}) async {
|
||||
didSkipDwds = skipDwds;
|
||||
return mockWebFs;
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@ -108,13 +89,22 @@ void main() {
|
||||
globals.fs.file('pubspec.yaml').createSync();
|
||||
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
||||
globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true);
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
when(mockWebDevFS.update(
|
||||
mainPath: anyNamed('mainPath'),
|
||||
target: anyNamed('target'),
|
||||
bundle: anyNamed('bundle'),
|
||||
firstBuildTime: anyNamed('firstBuildTime'),
|
||||
bundleFirstUpload: anyNamed('bundleFirstUpload'),
|
||||
generator: anyNamed('generator'),
|
||||
fullRestart: anyNamed('fullRestart'),
|
||||
dillOutputPath: anyNamed('dillOutputPath'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
pathToReload: anyNamed('pathToReload'),
|
||||
invalidatedFiles: anyNamed('invalidatedFiles'),
|
||||
trackWidgetCreation: true,
|
||||
)).thenAnswer((Invocation _) async {
|
||||
return UpdateFSReport(success: true, syncedBytes: 0)..invalidatedModules = <String>[];
|
||||
});
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) {
|
||||
return Future<bool>.value(false);
|
||||
});
|
||||
when(mockWebFs.uri).thenReturn('http://localhost:8765/app/');
|
||||
when(mockDebugConnection.vmService).thenReturn(mockVmService);
|
||||
when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) {
|
||||
return Completer<void>().future;
|
||||
@ -157,19 +147,6 @@ void main() {
|
||||
expect(residentWebRunner.debuggingEnabled, true);
|
||||
}));
|
||||
|
||||
test('runner with web server device does not initialize dwds', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
|
||||
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
|
||||
expect(didSkipDwds, true);
|
||||
}));
|
||||
|
||||
test('runner with web server device supports debugging with --start-paused', () => testbed.run(() {
|
||||
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
|
||||
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
|
||||
@ -185,33 +162,8 @@ void main() {
|
||||
expect(profileResidentWebRunner.debuggingEnabled, true);
|
||||
}));
|
||||
|
||||
test('runner with web server device uses debug extension with --start-paused', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
|
||||
final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner(
|
||||
mockFlutterDevice,
|
||||
flutterProject: FlutterProject.current(),
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
|
||||
ipv6: true,
|
||||
stayResident: true,
|
||||
dartDefines: <String>[],
|
||||
urlTunneller: null,
|
||||
) as ResidentWebRunner;
|
||||
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(runner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
|
||||
// Check connect() was told to use the debug extension.
|
||||
verify(mockWebFs.connect(true)).called(1);
|
||||
// And ensure the debug services was started.
|
||||
expect(testLogger.statusText, contains('Debug service listening on'));
|
||||
}));
|
||||
|
||||
test('profile does not supportsServiceProtocol', () => testbed.run(() {
|
||||
when(mockFlutterDevice.device).thenReturn(mockChromeDevice);
|
||||
when(mockFlutterDevice.device).thenReturn(mockChromeDevice);
|
||||
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
|
||||
mockFlutterDevice,
|
||||
flutterProject: FlutterProject.current(),
|
||||
@ -256,7 +208,7 @@ void main() {
|
||||
|
||||
verify(mockAppConnection.runMain()).called(1);
|
||||
verify(mockVmService.registerService('reloadSources', 'FlutterTools')).called(1);
|
||||
verify(status.stop()).called(2);
|
||||
verify(status.stop()).called(1);
|
||||
|
||||
expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/'));
|
||||
expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/');
|
||||
@ -333,37 +285,6 @@ void main() {
|
||||
}));
|
||||
|
||||
test('Can hot reload after attaching', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async {
|
||||
return true;
|
||||
});
|
||||
when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async {
|
||||
return Response.parse(<String, Object>{'type': 'Success'});
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
|
||||
|
||||
expect(testLogger.statusText, contains('Reloaded application in'));
|
||||
expect(result.code, 0);
|
||||
// ensure that analytics are sent.
|
||||
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
|
||||
'cd27': 'web-javascript',
|
||||
'cd28': null,
|
||||
'cd29': 'false',
|
||||
'cd30': 'true',
|
||||
})).called(1);
|
||||
verify(Usage.instance.sendTiming('hot', 'web-restart', any)).called(1);
|
||||
verify(Usage.instance.sendTiming('hot', 'web-refresh', any)).called(1);
|
||||
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
}));
|
||||
|
||||
test('Can hot reload after attaching - experimental', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
launchChromeInstance(mockChrome);
|
||||
when(mockWebDevFS.update(
|
||||
@ -399,19 +320,21 @@ void main() {
|
||||
expect(result.code, 0);
|
||||
verify(mockResidentCompiler.accept()).called(2);
|
||||
// ensure that analytics are sent.
|
||||
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
|
||||
'cd27': 'web-javascript',
|
||||
'cd28': null,
|
||||
'cd29': 'false',
|
||||
'cd30': 'true',
|
||||
})).called(1);
|
||||
final Map<String, String> config = verify(Usage.instance.sendEvent('hot', 'restart',
|
||||
parameters: captureAnyNamed('parameters'))).captured.first as Map<String, String>;
|
||||
|
||||
expect(config, allOf(<Matcher>[
|
||||
containsPair('cd27', 'web-javascript'),
|
||||
containsPair('cd28', null),
|
||||
containsPair('cd29', 'false'),
|
||||
containsPair('cd30', 'true'),
|
||||
]));
|
||||
verify(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('Can hot restart after attaching - experimental', () => testbed.run(() async {
|
||||
test('Can hot restart after attaching', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
launchChromeInstance(mockChrome);
|
||||
String entrypointFileName;
|
||||
@ -450,19 +373,21 @@ void main() {
|
||||
expect(result.code, 0);
|
||||
verify(mockResidentCompiler.accept()).called(2);
|
||||
// ensure that analytics are sent.
|
||||
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
|
||||
'cd27': 'web-javascript',
|
||||
'cd28': null,
|
||||
'cd29': 'false',
|
||||
'cd30': 'true',
|
||||
})).called(1);
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-incremental-restart', any));
|
||||
final Map<String, String> config = verify(Usage.instance.sendEvent('hot', 'restart',
|
||||
parameters: captureAnyNamed('parameters'))).captured.first as Map<String, String>;
|
||||
|
||||
expect(config, allOf(<Matcher>[
|
||||
containsPair('cd27', 'web-javascript'),
|
||||
containsPair('cd28', null),
|
||||
containsPair('cd29', 'false'),
|
||||
containsPair('cd30', 'true'),
|
||||
]));
|
||||
verify(Usage.instance.sendTiming('hot', 'web-incremental-restart', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('Can hot restart after attaching - experimental with web-server device', () => testbed.run(() async {
|
||||
test('Can hot restart after attaching with web-server device', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockFlutterDevice.device).thenReturn(mockWebServerDevice);
|
||||
when(mockWebDevFS.update(
|
||||
@ -493,71 +418,45 @@ void main() {
|
||||
expect(result.code, 0);
|
||||
verify(mockResidentCompiler.accept()).called(2);
|
||||
// ensure that analytics are sent.
|
||||
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
|
||||
'cd27': 'web-javascript',
|
||||
'cd28': null,
|
||||
'cd29': 'false',
|
||||
'cd30': 'true',
|
||||
})).called(1);
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-incremental-restart', any));
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('experimental resident runner is not debuggable', () => testbed.run(() {
|
||||
expect(residentWebRunner.debuggingEnabled, false);
|
||||
test('web resident runner iss debuggable', () => testbed.run(() {
|
||||
expect(residentWebRunner.debuggingEnabled, true);
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('Can hot restart after attaching', () => testbed.run(() async {
|
||||
test('Exits when initial compile fails', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockWebDevFS.update(
|
||||
mainPath: anyNamed('mainPath'),
|
||||
target: anyNamed('target'),
|
||||
bundle: anyNamed('bundle'),
|
||||
firstBuildTime: anyNamed('firstBuildTime'),
|
||||
bundleFirstUpload: anyNamed('bundleFirstUpload'),
|
||||
generator: anyNamed('generator'),
|
||||
fullRestart: anyNamed('fullRestart'),
|
||||
dillOutputPath: anyNamed('dillOutputPath'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
pathToReload: anyNamed('pathToReload'),
|
||||
invalidatedFiles: anyNamed('invalidatedFiles'),
|
||||
trackWidgetCreation: true,
|
||||
)).thenAnswer((Invocation _) async {
|
||||
return UpdateFSReport(success: false, syncedBytes: 0)..invalidatedModules = <String>[];
|
||||
});
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async {
|
||||
return true;
|
||||
});
|
||||
when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async {
|
||||
return Response.parse(<String, Object>{'type': 'Success'});
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(testLogger.statusText, contains('Restarted application in'));
|
||||
expect(result.code, 0);
|
||||
// ensure that analytics are sent.
|
||||
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
|
||||
'cd27': 'web-javascript',
|
||||
'cd28': null,
|
||||
'cd29': 'false',
|
||||
'cd30': 'true',
|
||||
})).called(1);
|
||||
expect(await residentWebRunner.run(), 1);
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
|
||||
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
}));
|
||||
|
||||
test('Selects Dwds runner in profile mode with incremental compiler enabled', () => testbed.run(() async {
|
||||
final ResidentWebRunner residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
|
||||
mockFlutterDevice,
|
||||
flutterProject: FlutterProject.current(),
|
||||
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
|
||||
ipv6: true,
|
||||
stayResident: true,
|
||||
dartDefines: const <String>[],
|
||||
urlTunneller: null,
|
||||
) as ResidentWebRunner;
|
||||
|
||||
expect(residentWebRunner.runtimeType.toString(), '_DwdsResidentWebRunner');
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
||||
}));
|
||||
|
||||
test('Fails on compilation errors in hot restart', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
@ -565,84 +464,80 @@ void main() {
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return false;
|
||||
when(mockWebDevFS.update(
|
||||
mainPath: anyNamed('mainPath'),
|
||||
target: anyNamed('target'),
|
||||
bundle: anyNamed('bundle'),
|
||||
firstBuildTime: anyNamed('firstBuildTime'),
|
||||
bundleFirstUpload: anyNamed('bundleFirstUpload'),
|
||||
generator: anyNamed('generator'),
|
||||
fullRestart: anyNamed('fullRestart'),
|
||||
dillOutputPath: anyNamed('dillOutputPath'),
|
||||
projectRootPath: anyNamed('projectRootPath'),
|
||||
pathToReload: anyNamed('pathToReload'),
|
||||
invalidatedFiles: anyNamed('invalidatedFiles'),
|
||||
trackWidgetCreation: true,
|
||||
)).thenAnswer((Invocation _) async {
|
||||
return UpdateFSReport(success: false, syncedBytes: 0)..invalidatedModules = <String>[];
|
||||
});
|
||||
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(result.code, 1);
|
||||
expect(result.message, contains('Failed to recompile application.'));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-recompile', any));
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
}));
|
||||
|
||||
test('Fails on vmservice response error for hot restart', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return true;
|
||||
});
|
||||
when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async {
|
||||
return Response.parse(<String, Object>{'type': 'Failed'});
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
// TODO(jonahwilliams): re-enable tests once we switch back to DWDS for hot reload/restart.
|
||||
// test('Fails on vmservice response error for hot restart', () => testbed.run(() async {
|
||||
// _setupMocks();
|
||||
// final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
// unawaited(residentWebRunner.run(
|
||||
// connectionInfoCompleter: connectionInfoCompleter,
|
||||
// ));
|
||||
// await connectionInfoCompleter.future;
|
||||
// when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async {
|
||||
// return Response.parse(<String, Object>{'type': 'Failed'});
|
||||
// });
|
||||
// final OperationResult result = await residentWebRunner.restart(fullRestart: true);
|
||||
|
||||
expect(result.code, 1);
|
||||
expect(result.message, contains('Failed'));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
|
||||
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
}));
|
||||
// expect(result.code, 1);
|
||||
// expect(result.message, contains('Failed'));
|
||||
// }));
|
||||
|
||||
test('Fails on vmservice response error for hot reload', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return true;
|
||||
});
|
||||
when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async {
|
||||
return Response.parse(<String, Object>{'type': 'Failed'});
|
||||
});
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
|
||||
// TODO(jonahwilliams): re-enable tests once we switch back to DWDS for hot reload/restart.
|
||||
// test('Fails on vmservice response error for hot reload', () => testbed.run(() async {
|
||||
// _setupMocks();
|
||||
// final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
// unawaited(residentWebRunner.run(
|
||||
// connectionInfoCompleter: connectionInfoCompleter,
|
||||
// ));
|
||||
// await connectionInfoCompleter.future;
|
||||
// when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async {
|
||||
// return Response.parse(<String, Object>{'type': 'Failed'});
|
||||
// });
|
||||
// final OperationResult result = await residentWebRunner.restart(fullRestart: false);
|
||||
|
||||
expect(result.code, 1);
|
||||
expect(result.message, contains('Failed'));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
|
||||
verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
|
||||
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockFlutterUsage(),
|
||||
}));
|
||||
// expect(result.code, 1);
|
||||
// expect(result.message, contains('Failed'));
|
||||
// }));
|
||||
|
||||
test('Fails on vmservice RpcError', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
|
||||
return true;
|
||||
});
|
||||
when(mockVmService.callServiceExtension('hotRestart')).thenThrow(RPCError('', 2, '123'));
|
||||
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
|
||||
// TODO(jonahwilliams): re-enable tests once we switch back to DWDS for hot reload/restart.
|
||||
// test('Fails on vmservice RpcError', () => testbed.run(() async {
|
||||
// _setupMocks();
|
||||
// final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
// unawaited(residentWebRunner.run(
|
||||
// connectionInfoCompleter: connectionInfoCompleter,
|
||||
// ));
|
||||
// await connectionInfoCompleter.future;
|
||||
// when(mockVmService.callServiceExtension('hotRestart')).thenThrow(RPCError('', 2, '123'));
|
||||
// final OperationResult result = await residentWebRunner.restart(fullRestart: false);
|
||||
|
||||
expect(result.code, 1);
|
||||
expect(result.message, contains('Page requires refresh'));
|
||||
}));
|
||||
// expect(result.code, 1);
|
||||
// expect(result.message, contains('Page requires refresh'));
|
||||
// }));
|
||||
|
||||
test('printHelp without details has web warning', () => testbed.run(() async {
|
||||
residentWebRunner.printHelp(details: false);
|
||||
@ -806,7 +701,7 @@ void main() {
|
||||
test('cleanup of resources is safe to call multiple times', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
bool debugClosed = false;
|
||||
when(mockChromeDevice.stopApp(any)).thenAnswer((Invocation invocation) async {
|
||||
when(mockDevice.stopApp(any)).thenAnswer((Invocation invocation) async {
|
||||
if (debugClosed) {
|
||||
throw StateError('debug connection closed twice');
|
||||
}
|
||||
@ -839,12 +734,11 @@ void main() {
|
||||
onDone.complete();
|
||||
|
||||
await result;
|
||||
verify(mockWebFs.stop()).called(1);
|
||||
}));
|
||||
|
||||
test('Prints target and device name on run', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockChromeDevice.name).thenReturn('Chromez');
|
||||
when(mockDevice.name).thenReturn('Chromez');
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
@ -857,6 +751,21 @@ void main() {
|
||||
test('Sends launched app.webLaunchUrl event for Chrome device', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockFlutterDevice.device).thenReturn(ChromeDevice());
|
||||
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
|
||||
return Uri.parse('http://localhost:8765/app/');
|
||||
});
|
||||
final MockChrome chrome = MockChrome();
|
||||
final MockChromeConnection mockChromeConnection = MockChromeConnection();
|
||||
final MockChromeTab mockChromeTab = MockChromeTab();
|
||||
final MockWipConnection mockWipConnection = MockWipConnection();
|
||||
when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async {
|
||||
return mockChromeTab;
|
||||
});
|
||||
when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async {
|
||||
return mockWipConnection;
|
||||
});
|
||||
when(chrome.chromeConnection).thenReturn(mockChromeConnection);
|
||||
launchChromeInstance(chrome);
|
||||
|
||||
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
|
||||
final MockStatus mockStatus = MockStatus();
|
||||
@ -893,6 +802,9 @@ void main() {
|
||||
test('Sends unlaunched app.webLaunchUrl event for Web Server device', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
|
||||
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
|
||||
return Uri.parse('http://localhost:8765/app/');
|
||||
});
|
||||
|
||||
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
|
||||
final MockStatus mockStatus = MockStatus();
|
||||
@ -924,203 +836,17 @@ void main() {
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => DelegateLogger(MockLogger())
|
||||
}));
|
||||
|
||||
test('Successfully turns WebSocketException into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw const WebSocketException();
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns AppConnectionException into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw AppConnectionException('Could not connect to application with appInstanceId: c0ae0750-ee91-11e9-cea6-35d95a968356');
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns ChromeDebugError into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw ChromeDebugException(<String, dynamic>{});
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns OptionsSkew error into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw OptionsSkew();
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns VersionSkew error into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw VersionSkew();
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns failed startup StateError error into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw StateError('Unable to start build daemon');
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
|
||||
test('Rethrows Exception type', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw Exception('Something went wrong');
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsException);
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Successfully turns MissingPortFile error into ToolExit', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw MissingPortFile();
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsToolExit());
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
}));
|
||||
|
||||
test('Rethrows unknown exception type from web tooling', () => testbed.run(() async {
|
||||
_setupMocks();
|
||||
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
|
||||
final MockStatus mockStatus = MockStatus();
|
||||
delegateLogger.status = mockStatus;
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
final Completer<void> unhandledErrorCompleter = Completer<void>();
|
||||
when(mockWebFs.connect(any)).thenAnswer((Invocation _) async {
|
||||
unawaited(unhandledErrorCompleter.future.then((void value) {
|
||||
throw StateError('Something went wrong');
|
||||
}));
|
||||
return ConnectionResult(mockAppConnection, mockDebugConnection);
|
||||
});
|
||||
|
||||
final Future<void> expectation = expectLater(() => residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
), throwsStateError);
|
||||
|
||||
unhandledErrorCompleter.complete();
|
||||
await expectation;
|
||||
verify(mockStatus.stop()).called(2);
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => DelegateLogger(BufferLogger(
|
||||
terminal: AnsiTerminal(
|
||||
stdio: null,
|
||||
platform: const LocalPlatform(),
|
||||
),
|
||||
outputPreferences: OutputPreferences.test(),
|
||||
))
|
||||
}));
|
||||
}
|
||||
|
||||
class MockChromeLauncher extends Mock implements ChromeLauncher {}
|
||||
class MockFlutterUsage extends Mock implements Usage {}
|
||||
class MockChromeDevice extends Mock implements ChromeDevice {}
|
||||
class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {}
|
||||
class MockFlutterWebFs extends Mock implements WebFs {}
|
||||
class MockDebugConnection extends Mock implements DebugConnection {}
|
||||
class MockAppConnection extends Mock implements AppConnection {}
|
||||
class MockVmService extends Mock implements VmService {}
|
||||
class MockStatus extends Mock implements Status {}
|
||||
class MockFlutterDevice extends Mock implements FlutterDevice {}
|
||||
class MockWebDevFS extends Mock implements DevFS {}
|
||||
class MockWebDevFS extends Mock implements WebDevFS {}
|
||||
class MockResidentCompiler extends Mock implements ResidentCompiler {}
|
||||
class MockChrome extends Mock implements Chrome {}
|
||||
class MockChromeConnection extends Mock implements ChromeConnection {}
|
||||
@ -1129,3 +855,4 @@ class MockWipConnection extends Mock implements WipConnection {}
|
||||
class MockWipDebugger extends Mock implements WipDebugger {}
|
||||
class MockLogger extends Mock implements Logger {}
|
||||
class MockWebServerDevice extends Mock implements WebServerDevice {}
|
||||
class MockDevice extends Mock implements Device {}
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
// stack trace mapper source is interpolated correctly.
|
||||
expect(result, contains('mapperEl.src = "mapper.js";'));
|
||||
// data-main is set to correct bootstrap module.
|
||||
expect(result, contains('requireEl.setAttribute("data-main", "main_module");'));
|
||||
expect(result, contains('requireEl.setAttribute("data-main", "main_module.bootstrap");'));
|
||||
// bootstrap main module has correct imports.
|
||||
expect(result, contains('require(["foo/bar/main.js", "dart_sdk"],'
|
||||
' function(app, dart_sdk) {'));
|
||||
@ -29,7 +29,7 @@ void main() {
|
||||
entrypoint: 'foo/bar/main.js',
|
||||
);
|
||||
// bootstrap main module has correct defined module.
|
||||
expect(result, contains('define("main_module", ["foo/bar/main.js", "dart_sdk"], '
|
||||
expect(result, contains('define("main_module.bootstrap", ["foo/bar/main.js", "dart_sdk"], '
|
||||
'function(app, dart_sdk) {'));
|
||||
});
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/web/devfs_web.dart';
|
||||
@ -15,8 +15,10 @@ import 'package:package_config/discovery.dart';
|
||||
import 'package:package_config/packages.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:shelf/shelf.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/io.dart';
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
const List<int> kTransparentImage = <int>[
|
||||
@ -28,75 +30,26 @@ const List<int> kTransparentImage = <int>[
|
||||
];
|
||||
|
||||
void main() {
|
||||
MockHttpServer mockHttpServer;
|
||||
StreamController<HttpRequest> requestController;
|
||||
Testbed testbed;
|
||||
MockHttpRequest request;
|
||||
MockHttpResponse response;
|
||||
MockHttpHeaders headers;
|
||||
Completer<void> closeCompleter;
|
||||
WebAssetServer webAssetServer;
|
||||
MockPlatform linux;
|
||||
Platform linux;
|
||||
Packages packages;
|
||||
MockPlatform windows;
|
||||
Platform windows;
|
||||
MockHttpServer mockHttpServer;
|
||||
|
||||
setUpAll(() async {
|
||||
packages = await loadPackagesFile(Uri.base.resolve('.packages'), loader: (Uri uri) async {
|
||||
return utf8.encode('\n');
|
||||
});
|
||||
packages = await loadPackagesFile(Uri.base.resolve('.packages'));
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
linux = MockPlatform();
|
||||
windows = MockPlatform();
|
||||
when(linux.isWindows).thenReturn(false);
|
||||
when(linux.environment).thenReturn(const <String, String>{});
|
||||
when(windows.environment).thenReturn(const <String, String>{});
|
||||
when(windows.isWindows).thenReturn(true);
|
||||
mockHttpServer = MockHttpServer();
|
||||
linux = FakePlatform(operatingSystem: 'linux', environment: <String, String>{});
|
||||
windows = FakePlatform(operatingSystem: 'windows', environment: <String, String>{});
|
||||
testbed = Testbed(setup: () {
|
||||
mockHttpServer = MockHttpServer();
|
||||
requestController = StreamController<HttpRequest>.broadcast();
|
||||
request = MockHttpRequest();
|
||||
response = MockHttpResponse();
|
||||
headers = MockHttpHeaders();
|
||||
closeCompleter = Completer<void>();
|
||||
when(mockHttpServer.listen(any, onError: anyNamed('onError'))).thenAnswer((Invocation invocation) {
|
||||
final void Function(HttpRequest) callback = invocation.positionalArguments.first as void Function(HttpRequest);
|
||||
return requestController.stream.listen(callback);
|
||||
});
|
||||
when(request.response).thenReturn(response);
|
||||
when(response.headers).thenReturn(headers);
|
||||
when(response.close()).thenAnswer((Invocation invocation) async {
|
||||
closeCompleter.complete();
|
||||
});
|
||||
webAssetServer = WebAssetServer(
|
||||
mockHttpServer, packages, InternetAddress.loopbackIPv4, onError: (dynamic error, StackTrace stackTrace) {
|
||||
closeCompleter.completeError(error, stackTrace);
|
||||
});
|
||||
webAssetServer = WebAssetServer(mockHttpServer, packages, InternetAddress.loopbackIPv4);
|
||||
});
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await webAssetServer.dispose();
|
||||
await requestController.close();
|
||||
});
|
||||
|
||||
test('Throws a tool exit if bind fails with a SocketException', () => testbed.run(() async {
|
||||
expect(WebAssetServer.start('hello', 1234), throwsToolExit());
|
||||
}));
|
||||
|
||||
test('Can catch exceptions through the onError callback', () => testbed.run(() async {
|
||||
when(response.close()).thenAnswer((Invocation invocation) {
|
||||
throw StateError('Something bad');
|
||||
});
|
||||
webAssetServer.writeFile('/foo.js', 'main() {}');
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js'));
|
||||
requestController.add(request);
|
||||
|
||||
expect(closeCompleter.future, throwsStateError);
|
||||
}));
|
||||
|
||||
test('Handles against malformed manifest', () => testbed.run(() async {
|
||||
final File source = globals.fs.file('source')
|
||||
..writeAsStringSync('main() {}');
|
||||
@ -131,13 +84,14 @@ void main() {
|
||||
}}));
|
||||
webAssetServer.write(source, manifest, sourcemap);
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'application/javascript')).called(1);
|
||||
verify(response.add(source.readAsBytesSync())).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', source.lengthSync().toString()),
|
||||
containsPair('content-type', 'application/javascript'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => linux,
|
||||
}));
|
||||
@ -145,13 +99,14 @@ void main() {
|
||||
test('serves JavaScript files from in memory cache not from manifest', () => testbed.run(() async {
|
||||
webAssetServer.writeFile('/foo.js', 'main() {}');
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.js')));
|
||||
|
||||
verify(headers.add('Content-Length', 9)).called(1);
|
||||
verify(headers.add('Content-Type', 'application/javascript')).called(1);
|
||||
verify(response.add(any)).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', '9'),
|
||||
containsPair('content-type', 'application/javascript'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, utf8.encode('main() {}'));
|
||||
}));
|
||||
|
||||
test('handles missing JavaScript files from in memory cache', () => testbed.run(() async {
|
||||
@ -166,11 +121,10 @@ void main() {
|
||||
}}));
|
||||
webAssetServer.write(source, manifest, sourcemap);
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/bar.js'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/bar.js')));
|
||||
|
||||
verify(response.statusCode = 404).called(1);
|
||||
expect(response.statusCode, HttpStatus.notFound);
|
||||
}));
|
||||
|
||||
test('serves JavaScript files from in memory cache on Windows', () => testbed.run(() async {
|
||||
@ -179,50 +133,35 @@ void main() {
|
||||
final File sourcemap = globals.fs.file('sourcemap')
|
||||
..writeAsStringSync('{}');
|
||||
final File manifest = globals.fs.file('manifest')
|
||||
..writeAsStringSync(json.encode(<String, Object>{'/C:/foo.js': <String, Object>{
|
||||
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <String, Object>{
|
||||
'code': <int>[0, source.lengthSync()],
|
||||
'sourcemap': <int>[0, 2],
|
||||
}}));
|
||||
webAssetServer.write(source, manifest, sourcemap);
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://localhost/foo.js')));
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/C:/foo.js'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'application/javascript')).called(1);
|
||||
verify(response.add(source.readAsBytesSync())).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', source.lengthSync().toString()),
|
||||
containsPair('content-type', 'application/javascript'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => windows,
|
||||
}));
|
||||
|
||||
test('serves Dart files from in filesystem on Windows', () => testbed.run(() async {
|
||||
final File source = globals.fs.file('foo.dart').absolute
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('void main() {}');
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/C:/foo.dart'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => windows,
|
||||
}));
|
||||
|
||||
test('serves asset files from in filesystem with known mime type on Windows', () => testbed.run(() async {
|
||||
final File source = globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'foo.png'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(kTransparentImage);
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo.png')));
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/assets/foo.png'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'image/png')).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', source.lengthSync().toString()),
|
||||
containsPair('content-type', 'image/png'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => windows,
|
||||
}));
|
||||
@ -232,22 +171,20 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('void main() {}');
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.dart'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.dart')));
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
expect(response.headers, containsPair('content-length', source.lengthSync().toString()));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => linux,
|
||||
}));
|
||||
|
||||
test('Handles missing Dart files from filesystem', () => testbed.run(() async {
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.dart'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/foo.dart')));
|
||||
|
||||
verify(response.statusCode = 404).called(1);
|
||||
expect(response.statusCode, HttpStatus.notFound);
|
||||
}));
|
||||
|
||||
test('serves asset files from in filesystem with known mime type', () => testbed.run(() async {
|
||||
@ -255,13 +192,14 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(kTransparentImage);
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/assets/foo.png'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo.png')));
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'image/png')).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', source.lengthSync().toString()),
|
||||
containsPair('content-type', 'image/png'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}));
|
||||
|
||||
test('serves asset files files from in filesystem with unknown mime type and length > 12', () => testbed.run(() async {
|
||||
@ -269,13 +207,14 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(List<int>.filled(100, 0));
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/assets/foo'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo')));
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'application/octet-stream')).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', '100'),
|
||||
containsPair('content-type', 'application/octet-stream'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}));
|
||||
|
||||
test('serves asset files files from in filesystem with unknown mime type and length < 12', () => testbed.run(() async {
|
||||
@ -283,21 +222,21 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(<int>[1, 2, 3]);
|
||||
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/assets/foo'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo')));
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'application/octet-stream')).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', '3'),
|
||||
containsPair('content-type', 'application/octet-stream'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}));
|
||||
|
||||
test('handles serving missing asset file', () => testbed.run(() async {
|
||||
when(request.uri).thenReturn(Uri.parse('http://foobar/assets/foo'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http://foobar/assets/foo')));
|
||||
|
||||
verify(response.statusCode = HttpStatus.notFound).called(1);
|
||||
expect(response.statusCode, HttpStatus.notFound);
|
||||
}));
|
||||
|
||||
test('serves /packages/<package>/<path> files as if they were '
|
||||
@ -307,14 +246,15 @@ void main() {
|
||||
final File source = globals.fs.file(globals.fs.path.fromUri(expectedUri))
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(<int>[1, 2, 3]);
|
||||
when(request.uri).thenReturn(
|
||||
Uri.parse('http:///packages/flutter_tools/foo.dart'));
|
||||
requestController.add(request);
|
||||
await closeCompleter.future;
|
||||
|
||||
verify(headers.add('Content-Length', source.lengthSync())).called(1);
|
||||
verify(headers.add('Content-Type', 'application/octet-stream')).called(1);
|
||||
verify(response.addStream(any)).called(1);
|
||||
final Response response = await webAssetServer
|
||||
.handleRequest(Request('GET', Uri.parse('http:///packages/flutter_tools/foo.dart')));
|
||||
|
||||
expect(response.headers, allOf(<Matcher>[
|
||||
containsPair('content-length', '3'),
|
||||
containsPair('content-type', 'application/octet-stream'),
|
||||
]));
|
||||
expect((await response.read().toList()).first, source.readAsBytesSync());
|
||||
}));
|
||||
|
||||
test('calling dispose closes the http server', () => testbed.run(() async {
|
||||
@ -324,46 +264,54 @@ void main() {
|
||||
}));
|
||||
|
||||
test('Can start web server with specified assets', () => testbed.run(() async {
|
||||
globals.fs.file('a.sources').writeAsStringSync('');
|
||||
globals.fs.file('a.json').writeAsStringSync('{}');
|
||||
globals.fs.file('a.map').writeAsStringSync('{}');
|
||||
globals.fs.file('.packages').writeAsStringSync('\n');
|
||||
await IOOverrides.runWithIOOverrides(() async {
|
||||
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
|
||||
..createSync(recursive: true);
|
||||
outputFile.parent.childFile('a.sources').writeAsStringSync('');
|
||||
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
|
||||
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
|
||||
outputFile.parent.childFile('.packages').writeAsStringSync('\n');
|
||||
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.recompile(
|
||||
any,
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packagesFilePath: anyNamed('packagesFilePath'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
final ResidentCompiler residentCompiler = MockResidentCompiler();
|
||||
when(residentCompiler.recompile(
|
||||
any,
|
||||
any,
|
||||
outputPath: anyNamed('outputPath'),
|
||||
packagesFilePath: anyNamed('packagesFilePath'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return const CompilerOutput('a', 0, <Uri>[]);
|
||||
});
|
||||
|
||||
final WebDevFS webDevFS = WebDevFS('localhost', 0, '.packages');
|
||||
webDevFS.requireJS.createSync(recursive: true);
|
||||
webDevFS.dartSdk.createSync(recursive: true);
|
||||
webDevFS.dartSdkSourcemap.createSync(recursive: true);
|
||||
webDevFS.stackTraceMapper.createSync(recursive: true);
|
||||
final WebDevFS webDevFS = WebDevFS(
|
||||
hostname: 'localhost',
|
||||
port: 0,
|
||||
packagesFilePath: '.packages',
|
||||
urlTunneller: null,
|
||||
buildMode: BuildMode.debug,
|
||||
enableDwds: false,
|
||||
);
|
||||
webDevFS.requireJS.createSync(recursive: true);
|
||||
webDevFS.dartSdk.createSync(recursive: true);
|
||||
webDevFS.dartSdkSourcemap.createSync(recursive: true);
|
||||
webDevFS.stackTraceMapper.createSync(recursive: true);
|
||||
|
||||
await webDevFS.create();
|
||||
await webDevFS.update(
|
||||
mainPath: globals.fs.path.join('lib', 'main.dart'),
|
||||
generator: residentCompiler,
|
||||
trackWidgetCreation: true,
|
||||
bundleFirstUpload: true,
|
||||
invalidatedFiles: <Uri>[],
|
||||
);
|
||||
await webDevFS.create();
|
||||
await webDevFS.update(
|
||||
mainPath: globals.fs.path.join('lib', 'main.dart'),
|
||||
generator: residentCompiler,
|
||||
trackWidgetCreation: true,
|
||||
bundleFirstUpload: true,
|
||||
invalidatedFiles: <Uri>[],
|
||||
);
|
||||
|
||||
expect(webDevFS.webAssetServer.getFile('/manifest.json'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('/flutter_service_worker.js'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('/manifest.json'), isNotNull);
|
||||
expect(webDevFS.webAssetServer.getFile('/flutter_service_worker.js'), isNotNull);
|
||||
|
||||
await webDevFS.destroy();
|
||||
}));
|
||||
await webDevFS.destroy();
|
||||
await webDevFS.dwds.stop();
|
||||
}, FlutterIOOverrides(fileSystem: globals.fs));
|
||||
}), skip: true); // Not clear the best way to test this, since shelf hits the real filesystem.
|
||||
}
|
||||
|
||||
class MockHttpServer extends Mock implements HttpServer {}
|
||||
class MockHttpRequest extends Mock implements HttpRequest {}
|
||||
class MockHttpResponse extends Mock implements HttpResponse {}
|
||||
class MockHttpHeaders extends Mock implements HttpHeaders {}
|
||||
class MockPlatform extends Mock implements Platform {}
|
||||
class MockResidentCompiler extends Mock implements ResidentCompiler {}
|
||||
|
@ -2,12 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/build_runner/web_fs.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/web/devfs_web.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -23,7 +20,6 @@ const List<int> kTransparentImage = <int>[
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
AssetServer assetServer;
|
||||
|
||||
setUp(() {
|
||||
testbed = Testbed(
|
||||
@ -39,80 +35,12 @@ void main() {
|
||||
globals.fs.file(globals.fs.path.join('build', 'flutter_assets', 'bar'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(<int>[1, 2, 3]);
|
||||
assetServer = DebugAssetServer(FlutterProject.current(), globals.fs.path.join('main'));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('can serve an html file from the web directory', () => testbed.run(() async {
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/index.html')));
|
||||
|
||||
expect(response.headers, <String, String>{
|
||||
'Content-Type': 'text/html',
|
||||
'content-length': '5',
|
||||
});
|
||||
expect(await response.readAsString(), 'hello');
|
||||
}));
|
||||
|
||||
test('can serve a sourcemap from dart:ui', () => testbed.run(() async {
|
||||
final String flutterWebSdkPath = globals.artifacts.getArtifactPath(Artifact.flutterWebSdk);
|
||||
final File windowSourceFile = globals.fs.file(globals.fs.path.join(flutterWebSdkPath, 'lib', 'ui', 'src', 'ui', 'window.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('test');
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/packages/build_web_compilers/lib/ui/src/ui/window.dart')));
|
||||
|
||||
expect(response.headers, <String, String>{
|
||||
'content-length': windowSourceFile.lengthSync().toString(),
|
||||
});
|
||||
expect(await response.readAsString(), 'test');
|
||||
}));
|
||||
|
||||
test('can serve a sourcemap from the dart:sdk', () => testbed.run(() async {
|
||||
final String dartSdkPath = globals.artifacts.getArtifactPath(Artifact.engineDartSdkPath);
|
||||
final File listSourceFile = globals.fs.file(globals.fs.path.join(dartSdkPath, 'lib', 'core', 'list.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('test');
|
||||
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/packages/dart-sdk/lib/core/list.dart')));
|
||||
|
||||
expect(response.headers, <String, String>{
|
||||
'content-length': listSourceFile.lengthSync().toString(),
|
||||
});
|
||||
expect(await response.readAsString(), 'test');
|
||||
}));
|
||||
|
||||
test('can serve an asset with a png content type', () => testbed.run(() async {
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/assets/foo.png')));
|
||||
|
||||
expect(response.headers, <String, String>{
|
||||
'Content-Type': 'image/png',
|
||||
'content-length': '64',
|
||||
});
|
||||
}));
|
||||
|
||||
test('can fallback to application/octet-stream', () => testbed.run(() async {
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/assets/bar')));
|
||||
|
||||
expect(response.headers, <String, String>{
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'content-length': '3',
|
||||
});
|
||||
}));
|
||||
|
||||
test('handles a missing html file from the web directory', () => testbed.run(() async {
|
||||
final Response response = await assetServer
|
||||
.handle(Request('GET', Uri.parse('http://localhost:8080/foobar.html')));
|
||||
|
||||
expect(response.statusCode, 404);
|
||||
}));
|
||||
|
||||
test('release asset server serves correct mime type and content length for png', () => testbed.run(() async {
|
||||
assetServer = ReleaseAssetServer();
|
||||
final ReleaseAssetServer assetServer = ReleaseAssetServer();
|
||||
globals.fs.file(globals.fs.path.join('build', 'web', 'assets', 'foo.png'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(kTransparentImage);
|
||||
@ -126,7 +54,7 @@ void main() {
|
||||
}));
|
||||
|
||||
test('release asset server serves correct mime type and content length for JavaScript', () => testbed.run(() async {
|
||||
assetServer = ReleaseAssetServer();
|
||||
final ReleaseAssetServer assetServer = ReleaseAssetServer();
|
||||
globals.fs.file(globals.fs.path.join('build', 'web', 'assets', 'foo.js'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('function main() {}');
|
||||
@ -140,7 +68,7 @@ void main() {
|
||||
}));
|
||||
|
||||
test('release asset server serves correct mime type and content length for html', () => testbed.run(() async {
|
||||
assetServer = ReleaseAssetServer();
|
||||
final ReleaseAssetServer assetServer = ReleaseAssetServer();
|
||||
globals.fs.file(globals.fs.path.join('build', 'web', 'assets', 'foo.html'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('<!doctype html><html></html>');
|
||||
@ -154,7 +82,7 @@ void main() {
|
||||
}));
|
||||
|
||||
test('release asset server serves content from flutter root', () => testbed.run(() async {
|
||||
assetServer = ReleaseAssetServer();
|
||||
final ReleaseAssetServer assetServer = ReleaseAssetServer();
|
||||
globals.fs.file(globals.fs.path.join('flutter', 'bar.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('void main() { }');
|
||||
@ -165,7 +93,7 @@ void main() {
|
||||
}));
|
||||
|
||||
test('release asset server serves content from project directory', () => testbed.run(() async {
|
||||
assetServer = ReleaseAssetServer();
|
||||
final ReleaseAssetServer assetServer = ReleaseAssetServer();
|
||||
globals.fs.file(globals.fs.path.join('lib', 'bar.dart'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('void main() { }');
|
||||
@ -174,4 +102,4 @@ void main() {
|
||||
|
||||
expect(response.statusCode, HttpStatus.ok);
|
||||
}));
|
||||
}
|
||||
}
|
@ -1,226 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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:build_daemon/client.dart';
|
||||
import 'package:build_daemon/data/build_status.dart';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:dwds/asset_handler.dart';
|
||||
import 'package:dwds/dwds.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/dart/pub.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/web/chrome.dart';
|
||||
import 'package:flutter_tools/src/build_runner/web_fs.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:http_multi_server/http_multi_server.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/mocks.dart';
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
void main() {
|
||||
Testbed testbed;
|
||||
MockBuildDaemonCreator mockBuildDaemonCreator;
|
||||
MockDwds mockDwds;
|
||||
MockChromeLauncher mockChromeLauncher;
|
||||
MockHttpMultiServer mockHttpMultiServer;
|
||||
MockBuildDaemonClient mockBuildDaemonClient;
|
||||
MockOperatingSystemUtils mockOperatingSystemUtils;
|
||||
MockProcessManager mockProcessManager;
|
||||
bool lastInitializePlatform;
|
||||
int lastPort;
|
||||
|
||||
setUp(() {
|
||||
lastPort = null;
|
||||
lastInitializePlatform = null;
|
||||
mockBuildDaemonCreator = MockBuildDaemonCreator();
|
||||
mockChromeLauncher = MockChromeLauncher();
|
||||
mockHttpMultiServer = MockHttpMultiServer();
|
||||
mockBuildDaemonClient = MockBuildDaemonClient();
|
||||
mockOperatingSystemUtils = MockOperatingSystemUtils();
|
||||
mockDwds = MockDwds();
|
||||
mockProcessManager = MockProcessManager();
|
||||
when(mockBuildDaemonCreator.startBuildDaemon(any, release: anyNamed('release'), initializePlatform: anyNamed('initializePlatform')))
|
||||
.thenAnswer((Invocation invocation) async {
|
||||
lastInitializePlatform = invocation.namedArguments[#initializePlatform] as bool;
|
||||
return mockBuildDaemonClient;
|
||||
});
|
||||
when(mockOperatingSystemUtils.findFreePort()).thenAnswer((Invocation _) async {
|
||||
return 1234;
|
||||
});
|
||||
when(mockProcessManager.start(
|
||||
any,
|
||||
workingDirectory: anyNamed('workingDirectory'),
|
||||
environment: anyNamed('environment'),
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
final String workingDirectory = invocation.namedArguments[#workingDirectory] as String;
|
||||
globals.fs.file(globals.fs.path.join(workingDirectory, '.packages')).createSync(recursive: true);
|
||||
return FakeProcess();
|
||||
});
|
||||
when(mockBuildDaemonClient.buildResults).thenAnswer((Invocation _) {
|
||||
return Stream<BuildResults>.fromFuture(Future<BuildResults>.value(
|
||||
BuildResults((BuildResultsBuilder builder) {
|
||||
builder.results = ListBuilder<BuildResult>(
|
||||
<BuildResult>[
|
||||
DefaultBuildResult((DefaultBuildResultBuilder builder) {
|
||||
builder.target = 'web';
|
||||
builder.status = BuildStatus.succeeded;
|
||||
}),
|
||||
],
|
||||
);
|
||||
})
|
||||
));
|
||||
});
|
||||
when(mockBuildDaemonCreator.assetServerPort(any)).thenReturn(4321);
|
||||
testbed = Testbed(
|
||||
setup: () {
|
||||
globals.fs.file(globals.fs.path.join('packages', 'flutter_tools', 'pubspec.yaml'))
|
||||
..createSync(recursive: true)
|
||||
..setLastModifiedSync(DateTime(1991, 08, 23));
|
||||
// Create an empty .packages file so we can read it when we check for
|
||||
// plugins on Webglobals.fs.start()
|
||||
globals.fs.file('.packages').createSync();
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
Pub: () => MockPub(),
|
||||
OperatingSystemUtils: () => mockOperatingSystemUtils,
|
||||
BuildDaemonCreator: () => mockBuildDaemonCreator,
|
||||
ChromeLauncher: () => mockChromeLauncher,
|
||||
ProcessManager: () => mockProcessManager,
|
||||
HttpMultiServerFactory: () => (dynamic address, int port) async {
|
||||
lastPort = port;
|
||||
return mockHttpMultiServer;
|
||||
},
|
||||
DwdsFactory: () => ({
|
||||
@required AssetHandler assetHandler,
|
||||
@required Stream<BuildResult> buildResults,
|
||||
@required ConnectionProvider chromeConnection,
|
||||
String hostname,
|
||||
ReloadConfiguration reloadConfiguration,
|
||||
bool serveDevTools,
|
||||
LogWriter logWriter,
|
||||
bool verbose,
|
||||
bool enableDebugExtension,
|
||||
UrlEncoder urlEncoder,
|
||||
}) async {
|
||||
return mockDwds;
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('Can create webFs from mocked interfaces', () => testbed.run(() async {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
await WebFs.start(
|
||||
skipDwds: false,
|
||||
target: globals.fs.path.join('lib', 'main.dart'),
|
||||
buildInfo: BuildInfo.debug,
|
||||
flutterProject: flutterProject,
|
||||
initializePlatform: true,
|
||||
hostname: null,
|
||||
port: null,
|
||||
urlTunneller: null,
|
||||
dartDefines: const <String>[],
|
||||
);
|
||||
// Since the .packages file is missing in the memory filesystem, this should
|
||||
// be called.
|
||||
verify(pub.get(
|
||||
context: PubContext.pubGet,
|
||||
directory: anyNamed('directory'),
|
||||
offline: true,
|
||||
skipPubspecYamlCheck: true,
|
||||
checkLastModified: false,
|
||||
)).called(1);
|
||||
|
||||
// The build daemon is told to build once.
|
||||
verify(mockBuildDaemonClient.startBuild()).called(1);
|
||||
|
||||
// .dart_tool directory is created.
|
||||
expect(flutterProject.dartTool.existsSync(), true);
|
||||
expect(lastInitializePlatform, true);
|
||||
}));
|
||||
|
||||
test('Can create webFs from mocked interfaces with initializePlatform', () => testbed.run(() async {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
await WebFs.start(
|
||||
skipDwds: false,
|
||||
target: globals.fs.path.join('lib', 'main.dart'),
|
||||
buildInfo: BuildInfo.debug,
|
||||
flutterProject: flutterProject,
|
||||
initializePlatform: false,
|
||||
hostname: null,
|
||||
port: null,
|
||||
urlTunneller: null,
|
||||
dartDefines: const <String>[],
|
||||
);
|
||||
|
||||
// The build daemon is told to build once.
|
||||
verify(mockBuildDaemonClient.startBuild()).called(1);
|
||||
|
||||
// .dart_tool directory is created.
|
||||
expect(flutterProject.dartTool.existsSync(), true);
|
||||
expect(lastInitializePlatform, false);
|
||||
}));
|
||||
|
||||
test('Uses provided port number and hostname.', () => testbed.run(() async {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
final WebFs webFs = await WebFs.start(
|
||||
skipDwds: false,
|
||||
target: globals.fs.path.join('lib', 'main.dart'),
|
||||
buildInfo: BuildInfo.debug,
|
||||
flutterProject: flutterProject,
|
||||
initializePlatform: false,
|
||||
hostname: 'localhost',
|
||||
port: '1234',
|
||||
urlTunneller: null,
|
||||
dartDefines: const <String>[],
|
||||
);
|
||||
|
||||
expect(webFs.uri.toString(), contains('localhost'));
|
||||
expect(lastPort, 1234);
|
||||
}));
|
||||
|
||||
test('Throws exception if build fails', () => testbed.run(() async {
|
||||
when(mockBuildDaemonClient.buildResults).thenAnswer((Invocation _) {
|
||||
return Stream<BuildResults>.fromFuture(Future<BuildResults>.value(
|
||||
BuildResults((BuildResultsBuilder builder) {
|
||||
builder.results = ListBuilder<BuildResult>(
|
||||
<BuildResult>[
|
||||
DefaultBuildResult((DefaultBuildResultBuilder builder) {
|
||||
builder.target = 'web';
|
||||
builder.status = BuildStatus.failed;
|
||||
}),
|
||||
],
|
||||
);
|
||||
})
|
||||
));
|
||||
});
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
|
||||
expect(WebFs.start(
|
||||
skipDwds: false,
|
||||
target: globals.fs.path.join('lib', 'main.dart'),
|
||||
buildInfo: BuildInfo.debug,
|
||||
flutterProject: flutterProject,
|
||||
initializePlatform: false,
|
||||
hostname: 'foo',
|
||||
port: '1234',
|
||||
urlTunneller: null,
|
||||
dartDefines: const <String>[],
|
||||
), throwsException);
|
||||
}));
|
||||
}
|
||||
|
||||
class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {}
|
||||
class MockBuildDaemonClient extends Mock implements BuildDaemonClient {}
|
||||
class MockDwds extends Mock implements Dwds {}
|
||||
class MockHttpMultiServer extends Mock implements HttpMultiServer {}
|
||||
class MockChromeLauncher extends Mock implements ChromeLauncher {}
|
||||
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
||||
class MockPub extends Mock implements Pub {}
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
@ -710,6 +710,9 @@ class MockResidentCompiler extends BasicMock implements ResidentCompiler {
|
||||
globals.fs.file(outputPath).writeAsStringSync('compiled_kernel_output');
|
||||
return CompilerOutput(outputPath, 0, <Uri>[]);
|
||||
}
|
||||
|
||||
@override
|
||||
void addFileSystemRoot(String root) { }
|
||||
}
|
||||
|
||||
/// A fake implementation of [ProcessResult].
|
||||
|
@ -722,7 +722,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
this.isWebEnabled = false,
|
||||
this.isWindowsEnabled = false,
|
||||
this.isAndroidEmbeddingV2Enabled = false,
|
||||
this.isWebIncrementalCompilerEnabled = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -740,9 +739,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
@override
|
||||
final bool isAndroidEmbeddingV2Enabled;
|
||||
|
||||
@override
|
||||
final bool isWebIncrementalCompilerEnabled;
|
||||
|
||||
@override
|
||||
bool isEnabled(Feature feature) {
|
||||
switch (feature) {
|
||||
@ -756,8 +752,6 @@ class TestFeatureFlags implements FeatureFlags {
|
||||
return isWindowsEnabled;
|
||||
case flutterAndroidEmbeddingV2Feature:
|
||||
return isAndroidEmbeddingV2Enabled;
|
||||
case flutterWebIncrementalCompiler:
|
||||
return isWebIncrementalCompilerEnabled;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user