Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
9f145f6c
Commit
9f145f6c
authored
Jan 14, 2020
by
Jonah Williams
Committed by
Flutter GitHub Bot
Jan 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools][web] Add basic service worker generation support to web applications (#48344)
parent
0a600e1d
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
146 additions
and
3 deletions
+146
-3
index.html
examples/flutter_gallery/web/index.html
+10
-0
web.dart
packages/flutter_tools/lib/src/build_system/targets/web.dart
+101
-1
assemble.dart
packages/flutter_tools/lib/src/commands/assemble.dart
+1
-1
compile.dart
packages/flutter_tools/lib/src/web/compile.dart
+1
-1
index.html.tmpl
packages/flutter_tools/templates/app/web/index.html.tmpl
+10
-0
web_test.dart
...ols/test/general.shard/build_system/targets/web_test.dart
+23
-0
No files found.
examples/flutter_gallery/web/index.html
View file @
9f145f6c
...
@@ -18,6 +18,16 @@ found in the LICENSE file. -->
...
@@ -18,6 +18,16 @@ found in the LICENSE file. -->
<link
rel=
"manifest"
href=
"/manifest.json"
>
<link
rel=
"manifest"
href=
"/manifest.json"
>
</head>
</head>
<body>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if
(
'serviceWorker'
in
navigator
)
{
window
.
addEventListener
(
'load'
,
function
()
{
navigator
.
serviceWorker
.
register
(
'/flutter_service_worker.js'
);
});
}
</script>
<script
src=
"main.dart.js"
type=
"application/javascript"
></script>
<script
src=
"main.dart.js"
type=
"application/javascript"
></script>
</body>
</body>
</html>
</html>
packages/flutter_tools/lib/src/build_system/targets/web.dart
View file @
9f145f6c
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:crypto/crypto.dart'
;
import
'../../artifacts.dart'
;
import
'../../artifacts.dart'
;
import
'../../base/file_system.dart'
;
import
'../../base/file_system.dart'
;
import
'../../base/io.dart'
;
import
'../../base/io.dart'
;
...
@@ -231,7 +233,12 @@ class WebReleaseBundle extends Target {
...
@@ -231,7 +233,12 @@ class WebReleaseBundle extends Target {
@override
@override
Future
<
void
>
build
(
Environment
environment
)
async
{
Future
<
void
>
build
(
Environment
environment
)
async
{
for
(
final
File
outputFile
in
environment
.
buildDir
.
listSync
(
recursive:
true
).
whereType
<
File
>())
{
for
(
final
File
outputFile
in
environment
.
buildDir
.
listSync
(
recursive:
true
).
whereType
<
File
>())
{
if
(!
globals
.
fs
.
path
.
basename
(
outputFile
.
path
).
contains
(
'main.dart.js'
))
{
final
String
basename
=
globals
.
fs
.
path
.
basename
(
outputFile
.
path
);
if
(!
basename
.
contains
(
'main.dart.js'
))
{
continue
;
}
// Do not copy the deps file.
if
(
basename
.
endsWith
(
'.deps'
))
{
continue
;
continue
;
}
}
outputFile
.
copySync
(
outputFile
.
copySync
(
...
@@ -267,3 +274,96 @@ class WebReleaseBundle extends Target {
...
@@ -267,3 +274,96 @@ class WebReleaseBundle extends Target {
}
}
}
}
/// Generate a service worker for a web target.
class
WebServiceWorker
extends
Target
{
const
WebServiceWorker
();
@override
String
get
name
=>
'web_service_worker'
;
@override
List
<
Target
>
get
dependencies
=>
const
<
Target
>[
Dart2JSTarget
(),
WebReleaseBundle
(),
];
@override
List
<
String
>
get
depfiles
=>
const
<
String
>[
'service_worker.d'
,
];
@override
List
<
Source
>
get
inputs
=>
const
<
Source
>[];
@override
List
<
Source
>
get
outputs
=>
const
<
Source
>[];
@override
Future
<
void
>
build
(
Environment
environment
)
async
{
final
List
<
File
>
contents
=
environment
.
outputDir
.
listSync
(
recursive:
true
)
.
whereType
<
File
>()
.
where
((
File
file
)
=>
!
file
.
path
.
endsWith
(
'flutter_service_worker.js'
)
&&
!
globals
.
fs
.
path
.
basename
(
file
.
path
).
startsWith
(
'.'
))
.
toList
();
// TODO(jonahwilliams): determine whether this needs to be made more efficient.
final
Map
<
String
,
String
>
uriToHash
=
<
String
,
String
>{
for
(
File
file
in
contents
)
// Do not force caching of source maps.
if
(!
file
.
path
.
endsWith
(
'main.dart.js.map'
))
'/
${globals.fs.path.relative(file.path, from: environment.outputDir.path)}
'
:
md5
.
convert
(
await
file
.
readAsBytes
()).
toString
(),
};
final
File
serviceWorkerFile
=
environment
.
outputDir
.
childFile
(
'flutter_service_worker.js'
);
final
Depfile
depfile
=
Depfile
(
contents
,
<
File
>[
serviceWorkerFile
]);
final
String
serviceWorker
=
generateServiceWorker
(
uriToHash
);
serviceWorkerFile
.
writeAsStringSync
(
serviceWorker
);
depfile
.
writeToFile
(
environment
.
buildDir
.
childFile
(
'service_worker.d'
));
}
}
/// Generate a service worker with an app-specific cache name a map of
/// resource files.
///
/// We embed file hashes directly into the worker so that the byte for byte
/// invalidation will automatically reactivate workers whenever a new
/// version is deployed.
// TODO(jonahwilliams): on re-activate, only evict stale assets.
String
generateServiceWorker
(
Map
<
String
,
String
>
resources
)
{
return
'''
'
use
strict
';
const CACHE_NAME = '
flutter
-
app
-
cache
';
const RESOURCES = {
${resources.entries.map((MapEntry<String, String> entry) => '"${entry.key}
": "
${entry.value}
"'
).
join
(
",
\n
"
)}
};
self
.
addEventListener
(
'activate'
,
function
(
event
)
{
event
.
waitUntil
(
caches
.
keys
().
then
(
function
(
cacheName
)
{
return
caches
.
delete
(
cacheName
);
}).
then
(
function
(
_
)
{
return
caches
.
open
(
CACHE_NAME
);
}).
then
(
function
(
cache
)
{
return
cache
.
addAll
(
Object
.
keys
(
RESOURCES
));
})
);
});
self
.
addEventListener
(
'fetch'
,
function
(
event
)
{
event
.
respondWith
(
caches
.
match
(
event
.
request
)
.
then
(
function
(
response
)
{
if
(
response
)
{
return
response
;
}
return
fetch
(
event
.
request
,
{
credentials:
'include'
});
})
);
});
''';
}
packages/flutter_tools/lib/src/commands/assemble.dart
View file @
9f145f6c
...
@@ -35,7 +35,7 @@ const List<Target> _kDefaultTargets = <Target>[
...
@@ -35,7 +35,7 @@ const List<Target> _kDefaultTargets = <Target>[
ProfileMacOSBundleFlutterAssets
(),
ProfileMacOSBundleFlutterAssets
(),
ReleaseMacOSBundleFlutterAssets
(),
ReleaseMacOSBundleFlutterAssets
(),
DebugBundleLinuxAssets
(),
DebugBundleLinuxAssets
(),
Web
ReleaseBundle
(),
Web
ServiceWorker
(),
DebugAndroidApplication
(),
DebugAndroidApplication
(),
FastStartAndroidApplication
(),
FastStartAndroidApplication
(),
ProfileAndroidApplication
(),
ProfileAndroidApplication
(),
...
...
packages/flutter_tools/lib/src/web/compile.dart
View file @
9f145f6c
...
@@ -39,7 +39,7 @@ Future<void> buildWeb(
...
@@ -39,7 +39,7 @@ Future<void> buildWeb(
final
Status
status
=
globals
.
logger
.
startProgress
(
'Compiling
$target
for the Web...'
,
timeout:
null
);
final
Status
status
=
globals
.
logger
.
startProgress
(
'Compiling
$target
for the Web...'
,
timeout:
null
);
final
Stopwatch
sw
=
Stopwatch
()..
start
();
final
Stopwatch
sw
=
Stopwatch
()..
start
();
try
{
try
{
final
BuildResult
result
=
await
buildSystem
.
build
(
const
Web
ReleaseBundle
(),
Environment
(
final
BuildResult
result
=
await
buildSystem
.
build
(
const
Web
ServiceWorker
(),
Environment
(
outputDir:
globals
.
fs
.
directory
(
getWebBuildDirectory
()),
outputDir:
globals
.
fs
.
directory
(
getWebBuildDirectory
()),
projectDir:
globals
.
fs
.
currentDirectory
,
projectDir:
globals
.
fs
.
currentDirectory
,
buildDir:
flutterProject
.
directory
buildDir:
flutterProject
.
directory
...
...
packages/flutter_tools/templates/app/web/index.html.tmpl
View file @
9f145f6c
...
@@ -15,6 +15,16 @@
...
@@ -15,6 +15,16 @@
<link
rel=
"manifest"
href=
"/manifest.json"
>
<link
rel=
"manifest"
href=
"/manifest.json"
>
</head>
</head>
<body>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if
(
'serviceWorker'
in
navigator
)
{
window
.
addEventListener
(
'load'
,
function
()
{
navigator
.
serviceWorker
.
register
(
'/service_worker.js'
);
});
}
</script>
<script
src=
"main.dart.js"
type=
"application/javascript"
></script>
<script
src=
"main.dart.js"
type=
"application/javascript"
></script>
</body>
</body>
</html>
</html>
packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
View file @
9f145f6c
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:file_testing/file_testing.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/build_system/build_system.dart'
;
import
'package:flutter_tools/src/build_system/build_system.dart'
;
...
@@ -80,6 +81,7 @@ void main() {
...
@@ -80,6 +81,7 @@ void main() {
}));
}));
test
(
'WebReleaseBundle copies dart2js output and resource files to output directory'
,
()
=>
testbed
.
run
(()
async
{
test
(
'WebReleaseBundle copies dart2js output and resource files to output directory'
,
()
=>
testbed
.
run
(()
async
{
environment
.
defines
[
kBuildMode
]
=
'release'
;
final
Directory
webResources
=
environment
.
projectDir
.
childDirectory
(
'web'
);
final
Directory
webResources
=
environment
.
projectDir
.
childDirectory
(
'web'
);
webResources
.
childFile
(
'index.html'
)
webResources
.
childFile
(
'index.html'
)
..
createSync
(
recursive:
true
);
..
createSync
(
recursive:
true
);
...
@@ -383,6 +385,27 @@ void main() {
...
@@ -383,6 +385,27 @@ void main() {
},
overrides:
<
Type
,
Generator
>{
},
overrides:
<
Type
,
Generator
>{
ProcessManager:
()
=>
MockProcessManager
(),
ProcessManager:
()
=>
MockProcessManager
(),
}));
}));
test
(
'Generated service worker correctly inlines file hashes'
,
()
{
final
String
result
=
generateServiceWorker
(<
String
,
String
>{
'/foo'
:
'abcd'
});
expect
(
result
,
contains
(
'{
\n
"/foo": "abcd"
\n
};'
));
});
test
(
'WebServiceWorker generates a service_worker for a web resource folder'
,
()
=>
testbed
.
run
(()
async
{
environment
.
outputDir
.
childFile
(
'a.txt'
)
..
createSync
(
recursive:
true
)
..
writeAsStringSync
(
'A'
);
await
const
WebServiceWorker
().
build
(
environment
);
expect
(
environment
.
outputDir
.
childFile
(
'flutter_service_worker.js'
),
exists
);
// Contains file hash.
expect
(
environment
.
outputDir
.
childFile
(
'flutter_service_worker.js'
).
readAsStringSync
(),
contains
(
'"/a.txt": "7fc56270e7a70fa81a5935b72eacbe29"'
));
expect
(
environment
.
buildDir
.
childFile
(
'service_worker.d'
),
exists
);
// Depends on resource file.
expect
(
environment
.
buildDir
.
childFile
(
'service_worker.d'
).
readAsStringSync
(),
contains
(
'a.txt'
));
}));
}
}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
class
MockProcessManager
extends
Mock
implements
ProcessManager
{}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment