Switch flutter_tools to use frontend_server for web compilation (#50365)

This commit is contained in:
Jonah Williams 2020-02-11 15:43:46 -08:00 committed by GitHub
parent 324e20de09
commit 18f38cd45b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 1065 additions and 2377 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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.

View File

@ -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 {

View File

@ -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].

View File

@ -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

View File

@ -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

View File

@ -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.');
}

View File

@ -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.

View File

@ -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('');
}
}

View File

@ -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

View File

@ -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];
}

View File

@ -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 {

View File

@ -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 {}

View File

@ -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 {}

View File

@ -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) {'));
});
}

View File

@ -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 {}

View File

@ -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);
}));
}
}

View File

@ -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 {}

View File

@ -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].

View File

@ -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;
}