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. -->
<link
rel=
"manifest"
href=
"/manifest.json"
>
</head>
<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>
</body>
</html>
packages/flutter_tools/lib/src/build_system/targets/web.dart
View file @
9f145f6c
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:crypto/crypto.dart'
;
import
'../../artifacts.dart'
;
import
'../../base/file_system.dart'
;
import
'../../base/io.dart'
;
...
...
@@ -231,7 +233,12 @@ class WebReleaseBundle extends Target {
@override
Future
<
void
>
build
(
Environment
environment
)
async
{
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
;
}
outputFile
.
copySync
(
...
...
@@ -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>[
ProfileMacOSBundleFlutterAssets
(),
ReleaseMacOSBundleFlutterAssets
(),
DebugBundleLinuxAssets
(),
Web
ReleaseBundle
(),
Web
ServiceWorker
(),
DebugAndroidApplication
(),
FastStartAndroidApplication
(),
ProfileAndroidApplication
(),
...
...
packages/flutter_tools/lib/src/web/compile.dart
View file @
9f145f6c
...
...
@@ -39,7 +39,7 @@ Future<void> buildWeb(
final
Status
status
=
globals
.
logger
.
startProgress
(
'Compiling
$target
for the Web...'
,
timeout:
null
);
final
Stopwatch
sw
=
Stopwatch
()..
start
();
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
()),
projectDir:
globals
.
fs
.
currentDirectory
,
buildDir:
flutterProject
.
directory
...
...
packages/flutter_tools/templates/app/web/index.html.tmpl
View file @
9f145f6c
...
...
@@ -15,6 +15,16 @@
<link
rel=
"manifest"
href=
"/manifest.json"
>
</head>
<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>
</body>
</html>
packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
View file @
9f145f6c
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// 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/build_system/build_system.dart'
;
...
...
@@ -80,6 +81,7 @@ void main() {
}));
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'
);
webResources
.
childFile
(
'index.html'
)
..
createSync
(
recursive:
true
);
...
...
@@ -383,6 +385,27 @@ void main() {
},
overrides:
<
Type
,
Generator
>{
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
{}
...
...
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