Unverified Commit 5d30c097 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools][web] Add support for web app manifests and arbitrary resource...

[flutter_tools][web] Add support for web app manifests and arbitrary resource files (from web/) (#48316)

parent 749589d8
...@@ -761,6 +761,13 @@ final Set<Hash256> _grandfatheredBinaries = <Hash256>{ ...@@ -761,6 +761,13 @@ final Set<Hash256> _grandfatheredBinaries = <Hash256>{
// (also used by a few examples) // (also used by a few examples)
Hash256(0xD29D4E0AF9256DC9, 0x2D0A8F8810608A5E, 0x64A132AD8B397CA2, 0xC4DDC0B1C26A68C3), Hash256(0xD29D4E0AF9256DC9, 0x2D0A8F8810608A5E, 0x64A132AD8B397CA2, 0xC4DDC0B1C26A68C3),
// packages/flutter_tools/templates/app/web/icons/Icon-192.png.copy.tmpl
// examples/flutter_gallery/web/icons/Icon-192.png
Hash256(0x3DCE99077602F704, 0x21C1C6B2A240BC9B, 0x83D64D86681D45F2, 0x154143310C980BE3),
// packages/flutter_tools/templates/app/web/icons/Icon-512.png.copy.tmpl
// examples/flutter_gallery/web/icons/Icon-512.png
Hash256(0xBACCB205AE45f0B4, 0x21BE1657259B4943, 0xAC40C95094AB877F, 0x3BCBE12CD544DCBE),
// GALLERY ICONS // GALLERY ICONS
...@@ -994,7 +1001,7 @@ Future<void> verifyNoBinaries(String workingDirectory, { Set<Hash256> grandfathe ...@@ -994,7 +1001,7 @@ Future<void> verifyNoBinaries(String workingDirectory, { Set<Hash256> grandfathe
assert( assert(
_grandfatheredBinaries _grandfatheredBinaries
.expand<int>((Hash256 hash) => <int>[hash.a, hash.b, hash.c, hash.d]) .expand<int>((Hash256 hash) => <int>[hash.a, hash.b, hash.c, hash.d])
.reduce((int value, int element) => value ^ element) == 0x39A050CD69434936 // Please do not modify this line. .reduce((int value, int element) => value ^ element) == 0xBFC18DE113B5AE8E // Please do not modify this line.
); );
grandfatheredBinaries ??= _grandfatheredBinaries; grandfatheredBinaries ??= _grandfatheredBinaries;
if (!Platform.isWindows) { // TODO(ianh): Port this to Windows if (!Platform.isWindows) { // TODO(ianh): Port this to Windows
......
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
...@@ -3,10 +3,21 @@ ...@@ -3,10 +3,21 @@
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. -->
<html> <html>
<head> <head>
<title>Flutter Gallery</title> <meta charset="UTF-8">
</head> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<body> <meta name="description" content="A demo app for Flutter's material design and cupertino widgets, as well as many other features of the Flutter SDK.">
<script src="main.dart.js"></script>
</body> <!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Flutter Gallery">
<link rel="apple-touch-icon" href="/icons/Icon-192.png">
<title>Flutter Gallery</title>
<link rel="manifest" href="/manifest.json">
</head>
<body>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html> </html>
{
"name": "flutter_gallery",
"short_name": "flutter_gallery",
"start_url": ".",
"display": "minimal-ui",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
...@@ -198,7 +198,7 @@ class Dart2JSTarget extends Target { ...@@ -198,7 +198,7 @@ class Dart2JSTarget extends Target {
} }
} }
/// Unpacks the dart2js compilation to a given output directory /// Unpacks the dart2js compilation and resources to a given output directory
class WebReleaseBundle extends Target { class WebReleaseBundle extends Target {
const WebReleaseBundle(); const WebReleaseBundle();
...@@ -214,18 +214,18 @@ class WebReleaseBundle extends Target { ...@@ -214,18 +214,18 @@ class WebReleaseBundle extends Target {
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/main.dart.js'), Source.pattern('{BUILD_DIR}/main.dart.js'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'), Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.pattern('{PROJECT_DIR}/web/index.html'),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/main.dart.js'), Source.pattern('{OUTPUT_DIR}/main.dart.js'),
Source.pattern('{OUTPUT_DIR}/index.html'),
]; ];
@override @override
List<String> get depfiles => const <String>[ List<String> get depfiles => const <String>[
'dart2js.d', 'dart2js.d',
'flutter_assets.d',
'web_resources.d',
]; ];
@override @override
...@@ -240,11 +240,30 @@ class WebReleaseBundle extends Target { ...@@ -240,11 +240,30 @@ class WebReleaseBundle extends Target {
} }
final Directory outputDirectory = environment.outputDir.childDirectory('assets'); final Directory outputDirectory = environment.outputDir.childDirectory('assets');
outputDirectory.createSync(recursive: true); outputDirectory.createSync(recursive: true);
environment.projectDir
.childDirectory('web')
.childFile('index.html')
.copySync(globals.fs.path.join(environment.outputDir.path, 'index.html'));
final Depfile depfile = await copyAssets(environment, environment.outputDir.childDirectory('assets')); final Depfile depfile = await copyAssets(environment, environment.outputDir.childDirectory('assets'));
depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d')); depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
final Directory webResources = environment.projectDir
.childDirectory('web');
final List<File> inputResourceFiles = webResources
.listSync(recursive: true)
.whereType<File>()
.toList();
// Copy other resource files out of web/ directory.
final List<File> outputResourcesFiles = <File>[];
for (final File inputFile in inputResourceFiles) {
final File outputFile = globals.fs.file(globals.fs.path.join(
environment.outputDir.path,
globals.fs.path.relative(inputFile.path, from: webResources.path)));
if (!outputFile.parent.existsSync()) {
outputFile.parent.createSync(recursive: true);
}
inputFile.copySync(outputFile.path);
outputResourcesFiles.add(outputFile);
}
final Depfile resourceFile = Depfile(inputResourceFiles, outputResourcesFiles);
resourceFile.writeToFile(environment.buildDir.childFile('web_resources.d'));
} }
} }
...@@ -2,7 +2,17 @@ ...@@ -2,7 +2,17 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="{{description}}">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="{{projectName}}">
<link rel="apple-touch-icon" href="/icons/Icon-192.png">
<title>{{projectName}}</title> <title>{{projectName}}</title>
<link rel="manifest" href="/manifest.json">
</head> </head>
<body> <body>
<script src="main.dart.js" type="application/javascript"></script> <script src="main.dart.js" type="application/javascript"></script>
......
{
"name": "{{projectName}}",
"short_name": "{{projectName}}",
"start_url": ".",
"display": "minimal-ui",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "{{description}}",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
...@@ -31,6 +31,7 @@ void main() { ...@@ -31,6 +31,7 @@ void main() {
when(mockPlatform.isWindows).thenReturn(false); when(mockPlatform.isWindows).thenReturn(false);
when(mockPlatform.isMacOS).thenReturn(true); when(mockPlatform.isMacOS).thenReturn(true);
when(mockPlatform.isLinux).thenReturn(false); when(mockPlatform.isLinux).thenReturn(false);
when(mockPlatform.environment).thenReturn(const <String, String>{});
when(mockWindowsPlatform.isWindows).thenReturn(true); when(mockWindowsPlatform.isWindows).thenReturn(true);
when(mockWindowsPlatform.isMacOS).thenReturn(false); when(mockWindowsPlatform.isMacOS).thenReturn(false);
...@@ -41,10 +42,11 @@ void main() { ...@@ -41,10 +42,11 @@ void main() {
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('foo:lib/\n'); ..writeAsStringSync('foo:lib/\n');
PackageMap.globalPackagesPath = packagesFile.path; PackageMap.globalPackagesPath = packagesFile.path;
globals.fs.currentDirectory.childDirectory('bar').createSync();
environment = Environment( environment = Environment(
projectDir: globals.fs.currentDirectory.childDirectory('foo'), projectDir: globals.fs.currentDirectory.childDirectory('foo'),
outputDir: globals.fs.currentDirectory, outputDir: globals.fs.currentDirectory.childDirectory('bar'),
buildDir: globals.fs.currentDirectory, buildDir: globals.fs.currentDirectory,
defines: <String, String>{ defines: <String, String>{
kTargetFile: globals.fs.path.join('foo', 'lib', 'main.dart'), kTargetFile: globals.fs.path.join('foo', 'lib', 'main.dart'),
...@@ -77,6 +79,32 @@ void main() { ...@@ -77,6 +79,32 @@ void main() {
expect(generated, contains("import 'package:foo/main.dart' as entrypoint;")); expect(generated, contains("import 'package:foo/main.dart' as entrypoint;"));
})); }));
test('WebReleaseBundle copies dart2js output and resource files to output directory', () => testbed.run(() async {
final Directory webResources = environment.projectDir.childDirectory('web');
webResources.childFile('index.html')
..createSync(recursive: true);
webResources.childFile('foo.txt')
..writeAsStringSync('A');
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle().build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'A');
expect(environment.outputDir.childFile('main.dart.js')
.existsSync(), true);
expect(environment.outputDir.childDirectory('assets')
.childFile('AssetManifest.json').existsSync(), true);
// Update to arbitary resource file triggers rebuild.
webResources.childFile('foo.txt').writeAsStringSync('B');
await const WebReleaseBundle().build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'B');
}));
test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async { test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async {
environment.defines[kTargetFile] = globals.fs.path.join('other', 'lib', 'main.dart'); environment.defines[kTargetFile] = globals.fs.path.join('other', 'lib', 'main.dart');
await const WebEntrypointTarget().build(environment); await const WebEntrypointTarget().build(environment);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment