[web] Put all index.html operations in one place (#118188)
* [web] Put all index.html operations in one place * review comments * fix build * change quotes * fix test
This commit is contained in:
parent
b0f1714b7b
commit
d4b6898478
@ -17,6 +17,7 @@ import '../../dart/language_version.dart';
|
|||||||
import '../../dart/package_map.dart';
|
import '../../dart/package_map.dart';
|
||||||
import '../../flutter_plugins.dart';
|
import '../../flutter_plugins.dart';
|
||||||
import '../../globals.dart' as globals;
|
import '../../globals.dart' as globals;
|
||||||
|
import '../../html_utils.dart';
|
||||||
import '../../project.dart';
|
import '../../project.dart';
|
||||||
import '../../web/compile.dart';
|
import '../../web/compile.dart';
|
||||||
import '../../web/file_generators/flutter_js.dart' as flutter_js;
|
import '../../web/file_generators/flutter_js.dart' as flutter_js;
|
||||||
@ -50,9 +51,6 @@ const String kCspMode = 'cspMode';
|
|||||||
/// Base href to set in index.html in flutter build command
|
/// Base href to set in index.html in flutter build command
|
||||||
const String kBaseHref = 'baseHref';
|
const String kBaseHref = 'baseHref';
|
||||||
|
|
||||||
/// Placeholder for base href
|
|
||||||
const String kBaseHrefPlaceholder = r'$FLUTTER_BASE_HREF';
|
|
||||||
|
|
||||||
/// The caching strategy to use for service worker generation.
|
/// The caching strategy to use for service worker generation.
|
||||||
const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
|
const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
|
||||||
|
|
||||||
@ -442,25 +440,12 @@ class WebReleaseBundle extends Target {
|
|||||||
// because it would need to be the hash for the entire bundle and not just the resource
|
// because it would need to be the hash for the entire bundle and not just the resource
|
||||||
// in question.
|
// in question.
|
||||||
if (environment.fileSystem.path.basename(inputFile.path) == 'index.html') {
|
if (environment.fileSystem.path.basename(inputFile.path) == 'index.html') {
|
||||||
final String randomHash = Random().nextInt(4294967296).toString();
|
final IndexHtml indexHtml = IndexHtml(inputFile.readAsStringSync());
|
||||||
String resultString = inputFile.readAsStringSync()
|
indexHtml.applySubstitutions(
|
||||||
.replaceFirst(
|
baseHref: environment.defines[kBaseHref] ?? '/',
|
||||||
'var serviceWorkerVersion = null',
|
serviceWorkerVersion: Random().nextInt(4294967296).toString(),
|
||||||
"var serviceWorkerVersion = '$randomHash'",
|
);
|
||||||
)
|
outputFile.writeAsStringSync(indexHtml.content);
|
||||||
// This is for legacy index.html that still use the old service
|
|
||||||
// worker loading mechanism.
|
|
||||||
.replaceFirst(
|
|
||||||
"navigator.serviceWorker.register('flutter_service_worker.js')",
|
|
||||||
"navigator.serviceWorker.register('flutter_service_worker.js?v=$randomHash')",
|
|
||||||
);
|
|
||||||
final String? baseHref = environment.defines[kBaseHref];
|
|
||||||
if (resultString.contains(kBaseHrefPlaceholder) && baseHref == null) {
|
|
||||||
resultString = resultString.replaceAll(kBaseHrefPlaceholder, '/');
|
|
||||||
} else if (resultString.contains(kBaseHrefPlaceholder) && baseHref != null) {
|
|
||||||
resultString = resultString.replaceAll(kBaseHrefPlaceholder, baseHref);
|
|
||||||
}
|
|
||||||
outputFile.writeAsStringSync(resultString);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
inputFile.copySync(outputFile.path);
|
inputFile.copySync(outputFile.path);
|
||||||
|
@ -7,6 +7,7 @@ import '../base/file_system.dart';
|
|||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
import '../build_system/targets/web.dart';
|
import '../build_system/targets/web.dart';
|
||||||
import '../features.dart';
|
import '../features.dart';
|
||||||
|
import '../html_utils.dart';
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../runner/flutter_command.dart'
|
import '../runner/flutter_command.dart'
|
||||||
show DevelopmentArtifact, FlutterCommandResult;
|
show DevelopmentArtifact, FlutterCommandResult;
|
||||||
@ -127,7 +128,7 @@ class BuildWebCommand extends BuildSubCommand {
|
|||||||
baseHref != null) {
|
baseHref != null) {
|
||||||
throwToolExit(
|
throwToolExit(
|
||||||
"Couldn't find the placeholder for base href. "
|
"Couldn't find the placeholder for base href. "
|
||||||
r'Please add `<base href="$FLUTTER_BASE_HREF">` to web/index.html'
|
'Please add `<base href="$kBaseHrefPlaceholder">` to web/index.html'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
112
packages/flutter_tools/lib/src/html_utils.dart
Normal file
112
packages/flutter_tools/lib/src/html_utils.dart
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// 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:html/dom.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
|
||||||
|
import 'base/common.dart';
|
||||||
|
|
||||||
|
/// Placeholder for base href
|
||||||
|
const String kBaseHrefPlaceholder = r'$FLUTTER_BASE_HREF';
|
||||||
|
|
||||||
|
/// Utility class for parsing and performing operations on the contents of the
|
||||||
|
/// index.html file.
|
||||||
|
///
|
||||||
|
/// For example, to parse the base href from the index.html file:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// String parseBaseHref(File indexHtmlFile) {
|
||||||
|
/// final IndexHtml indexHtml = IndexHtml(indexHtmlFile.readAsStringSync());
|
||||||
|
/// return indexHtml.getBaseHref();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
class IndexHtml {
|
||||||
|
IndexHtml(this._content);
|
||||||
|
|
||||||
|
String get content => _content;
|
||||||
|
String _content;
|
||||||
|
|
||||||
|
Document _getDocument() => parse(_content);
|
||||||
|
|
||||||
|
/// Parses the base href from the index.html file.
|
||||||
|
String getBaseHref() {
|
||||||
|
final Element? baseElement = _getDocument().querySelector('base');
|
||||||
|
final String? baseHref = baseElement?.attributes == null
|
||||||
|
? null
|
||||||
|
: baseElement!.attributes['href'];
|
||||||
|
|
||||||
|
if (baseHref == null || baseHref == kBaseHrefPlaceholder) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseHref.startsWith('/')) {
|
||||||
|
throw ToolExit(
|
||||||
|
'Error: The base href in "web/index.html" must be absolute (i.e. start '
|
||||||
|
'with a "/"), but found: `${baseElement!.outerHtml}`.\n'
|
||||||
|
'$_kBasePathExample',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseHref.endsWith('/')) {
|
||||||
|
throw ToolExit(
|
||||||
|
'Error: The base href in "web/index.html" must end with a "/", but found: `${baseElement!.outerHtml}`.\n'
|
||||||
|
'$_kBasePathExample',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stripLeadingSlash(stripTrailingSlash(baseHref));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies substitutions to the content of the index.html file.
|
||||||
|
void applySubstitutions({
|
||||||
|
required String baseHref,
|
||||||
|
required String? serviceWorkerVersion,
|
||||||
|
}) {
|
||||||
|
if (_content.contains(kBaseHrefPlaceholder)) {
|
||||||
|
_content = _content.replaceAll(kBaseHrefPlaceholder, baseHref);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceWorkerVersion != null) {
|
||||||
|
_content = _content
|
||||||
|
.replaceFirst(
|
||||||
|
'var serviceWorkerVersion = null',
|
||||||
|
'var serviceWorkerVersion = "$serviceWorkerVersion"',
|
||||||
|
)
|
||||||
|
// This is for legacy index.html that still uses the old service
|
||||||
|
// worker loading mechanism.
|
||||||
|
.replaceFirst(
|
||||||
|
"navigator.serviceWorker.register('flutter_service_worker.js')",
|
||||||
|
"navigator.serviceWorker.register('flutter_service_worker.js?v=$serviceWorkerVersion')",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strips the leading slash from a path.
|
||||||
|
String stripLeadingSlash(String path) {
|
||||||
|
while (path.startsWith('/')) {
|
||||||
|
path = path.substring(1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Strips the trailing slash from a path.
|
||||||
|
String stripTrailingSlash(String path) {
|
||||||
|
while (path.endsWith('/')) {
|
||||||
|
path = path.substring(0, path.length - 1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String _kBasePathExample = '''
|
||||||
|
For example, to serve from the root use:
|
||||||
|
|
||||||
|
<base href="/">
|
||||||
|
|
||||||
|
To serve from a subpath "foo" (i.e. http://localhost:8080/foo/ instead of http://localhost:8080/) use:
|
||||||
|
|
||||||
|
<base href="/foo/">
|
||||||
|
|
||||||
|
For more information, see: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||||
|
''';
|
@ -8,8 +8,6 @@ import 'dart:typed_data';
|
|||||||
import 'package:dwds/data/build_result.dart';
|
import 'package:dwds/data/build_result.dart';
|
||||||
// ignore: import_of_legacy_library_into_null_safe
|
// ignore: import_of_legacy_library_into_null_safe
|
||||||
import 'package:dwds/dwds.dart';
|
import 'package:dwds/dwds.dart';
|
||||||
import 'package:html/dom.dart';
|
|
||||||
import 'package:html/parser.dart';
|
|
||||||
import 'package:logging/logging.dart' as logging;
|
import 'package:logging/logging.dart' as logging;
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:mime/mime.dart' as mime;
|
import 'package:mime/mime.dart' as mime;
|
||||||
@ -29,7 +27,6 @@ import '../base/platform.dart';
|
|||||||
import '../build_info.dart';
|
import '../build_info.dart';
|
||||||
import '../build_system/targets/scene_importer.dart';
|
import '../build_system/targets/scene_importer.dart';
|
||||||
import '../build_system/targets/shader_compiler.dart';
|
import '../build_system/targets/shader_compiler.dart';
|
||||||
import '../build_system/targets/web.dart';
|
|
||||||
import '../bundle_builder.dart';
|
import '../bundle_builder.dart';
|
||||||
import '../cache.dart';
|
import '../cache.dart';
|
||||||
import '../compile.dart';
|
import '../compile.dart';
|
||||||
@ -37,6 +34,7 @@ import '../convert.dart';
|
|||||||
import '../dart/package_map.dart';
|
import '../dart/package_map.dart';
|
||||||
import '../devfs.dart';
|
import '../devfs.dart';
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
|
import '../html_utils.dart';
|
||||||
import '../project.dart';
|
import '../project.dart';
|
||||||
import '../vmservice.dart';
|
import '../vmservice.dart';
|
||||||
import '../web/bootstrap.dart';
|
import '../web/bootstrap.dart';
|
||||||
@ -136,9 +134,7 @@ class WebAssetServer implements AssetReader {
|
|||||||
this._modules,
|
this._modules,
|
||||||
this._digests,
|
this._digests,
|
||||||
this._nullSafetyMode,
|
this._nullSafetyMode,
|
||||||
) : basePath = _parseBasePathFromIndexHtml(globals.fs.currentDirectory
|
) : basePath = _getIndexHtml().getBaseHref();
|
||||||
.childDirectory('web')
|
|
||||||
.childFile('index.html'));
|
|
||||||
|
|
||||||
// Fallback to "application/octet-stream" on null which
|
// Fallback to "application/octet-stream" on null which
|
||||||
// makes no claims as to the structure of the data.
|
// makes no claims as to the structure of the data.
|
||||||
@ -299,7 +295,7 @@ class WebAssetServer implements AssetReader {
|
|||||||
server,
|
server,
|
||||||
PackageUriMapper(packageConfig),
|
PackageUriMapper(packageConfig),
|
||||||
digestProvider,
|
digestProvider,
|
||||||
server.basePath!,
|
server.basePath,
|
||||||
).strategy,
|
).strategy,
|
||||||
expressionCompiler: expressionCompiler,
|
expressionCompiler: expressionCompiler,
|
||||||
spawnDds: enableDds,
|
spawnDds: enableDds,
|
||||||
@ -345,12 +341,11 @@ class WebAssetServer implements AssetReader {
|
|||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Uint8List? getMetadata(String path) => _webMemoryFS.metadataFiles[path];
|
Uint8List? getMetadata(String path) => _webMemoryFS.metadataFiles[path];
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
|
|
||||||
/// The base path to serve from.
|
/// The base path to serve from.
|
||||||
///
|
///
|
||||||
/// It should have no leading or trailing slashes.
|
/// It should have no leading or trailing slashes.
|
||||||
String? basePath = '';
|
@visibleForTesting
|
||||||
|
String basePath;
|
||||||
|
|
||||||
// handle requests for JavaScript source, dart sources maps, or asset files.
|
// handle requests for JavaScript source, dart sources maps, or asset files.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
@ -496,27 +491,20 @@ class WebAssetServer implements AssetReader {
|
|||||||
WebRendererMode webRenderer = WebRendererMode.html;
|
WebRendererMode webRenderer = WebRendererMode.html;
|
||||||
|
|
||||||
shelf.Response _serveIndex() {
|
shelf.Response _serveIndex() {
|
||||||
|
|
||||||
|
final IndexHtml indexHtml = _getIndexHtml();
|
||||||
|
|
||||||
|
indexHtml.applySubstitutions(
|
||||||
|
// Currently, we don't support --base-href for the "run" command.
|
||||||
|
baseHref: '/',
|
||||||
|
serviceWorkerVersion: null,
|
||||||
|
);
|
||||||
|
|
||||||
final Map<String, String> headers = <String, String>{
|
final Map<String, String> headers = <String, String>{
|
||||||
HttpHeaders.contentTypeHeader: 'text/html',
|
HttpHeaders.contentTypeHeader: 'text/html',
|
||||||
|
HttpHeaders.contentLengthHeader: indexHtml.content.length.toString(),
|
||||||
};
|
};
|
||||||
final File indexFile = globals.fs.currentDirectory
|
return shelf.Response.ok(indexHtml.content, headers: headers);
|
||||||
.childDirectory('web')
|
|
||||||
.childFile('index.html');
|
|
||||||
|
|
||||||
if (indexFile.existsSync()) {
|
|
||||||
String indexFileContent = indexFile.readAsStringSync();
|
|
||||||
if (indexFileContent.contains(kBaseHrefPlaceholder)) {
|
|
||||||
indexFileContent = indexFileContent.replaceAll(kBaseHrefPlaceholder, '/');
|
|
||||||
headers[HttpHeaders.contentLengthHeader] = indexFileContent.length.toString();
|
|
||||||
return shelf.Response.ok(indexFileContent,headers: headers);
|
|
||||||
}
|
|
||||||
headers[HttpHeaders.contentLengthHeader] =
|
|
||||||
indexFile.lengthSync().toString();
|
|
||||||
return shelf.Response.ok(indexFile.openRead(), headers: headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers[HttpHeaders.contentLengthHeader] = _kDefaultIndex.length.toString();
|
|
||||||
return shelf.Response.ok(_kDefaultIndex, headers: headers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to resolve `path` to a dart file.
|
// Attempt to resolve `path` to a dart file.
|
||||||
@ -783,9 +771,9 @@ class WebDevFS implements DevFS {
|
|||||||
webAssetServer.webRenderer = WebRendererMode.canvaskit;
|
webAssetServer.webRenderer = WebRendererMode.canvaskit;
|
||||||
}
|
}
|
||||||
if (hostname == 'any') {
|
if (hostname == 'any') {
|
||||||
_baseUri = Uri.http('localhost:$selectedPort', webAssetServer.basePath!);
|
_baseUri = Uri.http('localhost:$selectedPort', webAssetServer.basePath);
|
||||||
} else {
|
} else {
|
||||||
_baseUri = Uri.http('$hostname:$selectedPort', webAssetServer.basePath!);
|
_baseUri = Uri.http('$hostname:$selectedPort', webAssetServer.basePath);
|
||||||
}
|
}
|
||||||
return _baseUri!;
|
return _baseUri!;
|
||||||
}
|
}
|
||||||
@ -977,12 +965,11 @@ class ReleaseAssetServer {
|
|||||||
final FileSystemUtils _fileSystemUtils;
|
final FileSystemUtils _fileSystemUtils;
|
||||||
final Platform _platform;
|
final Platform _platform;
|
||||||
|
|
||||||
@visibleForTesting
|
|
||||||
|
|
||||||
/// The base path to serve from.
|
/// The base path to serve from.
|
||||||
///
|
///
|
||||||
/// It should have no leading or trailing slashes.
|
/// It should have no leading or trailing slashes.
|
||||||
final String? basePath;
|
@visibleForTesting
|
||||||
|
final String basePath;
|
||||||
|
|
||||||
// Locations where source files, assets, or source maps may be located.
|
// Locations where source files, assets, or source maps may be located.
|
||||||
List<Uri> _searchPaths() => <Uri>[
|
List<Uri> _searchPaths() => <Uri>[
|
||||||
@ -1070,67 +1057,21 @@ Future<Directory> _loadDwdsDirectory(
|
|||||||
return fileSystem.directory(packageConfig['dwds']!.packageUriRoot);
|
return fileSystem.directory(packageConfig['dwds']!.packageUriRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _stripBasePath(String path, String? basePath) {
|
String? _stripBasePath(String path, String basePath) {
|
||||||
path = _stripLeadingSlashes(path);
|
path = stripLeadingSlash(path);
|
||||||
if (basePath != null && path.startsWith(basePath)) {
|
if (path.startsWith(basePath)) {
|
||||||
path = path.substring(basePath.length);
|
path = path.substring(basePath.length);
|
||||||
} else {
|
} else {
|
||||||
// The given path isn't under base path, return null to indicate that.
|
// The given path isn't under base path, return null to indicate that.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return _stripLeadingSlashes(path);
|
return stripLeadingSlash(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _stripLeadingSlashes(String path) {
|
IndexHtml _getIndexHtml() {
|
||||||
while (path.startsWith('/')) {
|
final File indexHtml =
|
||||||
path = path.substring(1);
|
globals.fs.currentDirectory.childDirectory('web').childFile('index.html');
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _stripTrailingSlashes(String path) {
|
|
||||||
while (path.endsWith('/')) {
|
|
||||||
path = path.substring(0, path.length - 1);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _parseBasePathFromIndexHtml(File indexHtml) {
|
|
||||||
final String htmlContent =
|
final String htmlContent =
|
||||||
indexHtml.existsSync() ? indexHtml.readAsStringSync() : _kDefaultIndex;
|
indexHtml.existsSync() ? indexHtml.readAsStringSync() : _kDefaultIndex;
|
||||||
final Document document = parse(htmlContent);
|
return IndexHtml(htmlContent);
|
||||||
final Element? baseElement = document.querySelector('base');
|
|
||||||
String? baseHref =
|
|
||||||
baseElement?.attributes == null ? null : baseElement!.attributes['href'];
|
|
||||||
|
|
||||||
if (baseHref == null || baseHref == kBaseHrefPlaceholder) {
|
|
||||||
baseHref = '';
|
|
||||||
} else if (!baseHref.startsWith('/')) {
|
|
||||||
throw ToolExit(
|
|
||||||
'Error: The base href in "web/index.html" must be absolute (i.e. start '
|
|
||||||
'with a "/"), but found: `${baseElement!.outerHtml}`.\n'
|
|
||||||
'$basePathExample',
|
|
||||||
);
|
|
||||||
} else if (!baseHref.endsWith('/')) {
|
|
||||||
throw ToolExit(
|
|
||||||
'Error: The base href in "web/index.html" must end with a "/", but found: `${baseElement!.outerHtml}`.\n'
|
|
||||||
'$basePathExample',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
baseHref = _stripLeadingSlashes(_stripTrailingSlashes(baseHref));
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseHref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const String basePathExample = '''
|
|
||||||
For example, to serve from the root use:
|
|
||||||
|
|
||||||
<base href="/">
|
|
||||||
|
|
||||||
To serve from a subpath "foo" (i.e. http://localhost:8080/foo/ instead of http://localhost:8080/) use:
|
|
||||||
|
|
||||||
<base href="/foo/">
|
|
||||||
|
|
||||||
For more information, see: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
|
||||||
''';
|
|
||||||
|
@ -372,7 +372,6 @@ class ResidentWebRunner extends ResidentRunner {
|
|||||||
true,
|
true,
|
||||||
debuggingOptions.nativeNullAssertions,
|
debuggingOptions.nativeNullAssertions,
|
||||||
false,
|
false,
|
||||||
baseHref: kBaseHref,
|
|
||||||
);
|
);
|
||||||
} on ToolExit {
|
} on ToolExit {
|
||||||
return OperationResult(1, 'Failed to recompile application.');
|
return OperationResult(1, 'Failed to recompile application.');
|
||||||
|
@ -12,6 +12,7 @@ import 'package:flutter_tools/src/build_system/build_system.dart';
|
|||||||
import 'package:flutter_tools/src/build_system/depfile.dart';
|
import 'package:flutter_tools/src/build_system/depfile.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/web.dart';
|
import 'package:flutter_tools/src/build_system/targets/web.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:flutter_tools/src/html_utils.dart';
|
||||||
import 'package:flutter_tools/src/isolated/mustache_template.dart';
|
import 'package:flutter_tools/src/isolated/mustache_template.dart';
|
||||||
import 'package:flutter_tools/src/web/compile.dart';
|
import 'package:flutter_tools/src/web/compile.dart';
|
||||||
import 'package:flutter_tools/src/web/file_generators/flutter_js.dart' as flutter_js;
|
import 'package:flutter_tools/src/web/file_generators/flutter_js.dart' as flutter_js;
|
||||||
|
141
packages/flutter_tools/test/general.shard/html_utils_test.dart
Normal file
141
packages/flutter_tools/test/general.shard/html_utils_test.dart
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// 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:flutter_tools/src/html_utils.dart';
|
||||||
|
|
||||||
|
import '../src/common.dart';
|
||||||
|
|
||||||
|
const String htmlSample1 = '''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<base href="/foo/222/">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div></div>
|
||||||
|
<script src="main.dart.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''';
|
||||||
|
|
||||||
|
const String htmlSample2 = '''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<base href="$kBaseHrefPlaceholder">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div></div>
|
||||||
|
<script src="main.dart.js"></script>
|
||||||
|
<script>
|
||||||
|
var serviceWorkerVersion = null;
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
navigator.serviceWorker.register('flutter_service_worker.js');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''';
|
||||||
|
|
||||||
|
String htmlSample2Replaced({
|
||||||
|
required String baseHref,
|
||||||
|
required String serviceWorkerVersion,
|
||||||
|
}) =>
|
||||||
|
'''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<base href="$baseHref">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div></div>
|
||||||
|
<script src="main.dart.js"></script>
|
||||||
|
<script>
|
||||||
|
var serviceWorkerVersion = "$serviceWorkerVersion";
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
navigator.serviceWorker.register('flutter_service_worker.js?v=$serviceWorkerVersion');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''';
|
||||||
|
|
||||||
|
const String htmlSample3 = '''
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div></div>
|
||||||
|
<script src="main.dart.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('can parse baseHref', () {
|
||||||
|
expect(IndexHtml('<base href="/foo/111/">').getBaseHref(), 'foo/111');
|
||||||
|
expect(IndexHtml(htmlSample1).getBaseHref(), 'foo/222');
|
||||||
|
expect(IndexHtml(htmlSample2).getBaseHref(), ''); // Placeholder base href.
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles missing baseHref', () {
|
||||||
|
expect(IndexHtml('').getBaseHref(), '');
|
||||||
|
expect(IndexHtml('<base>').getBaseHref(), '');
|
||||||
|
expect(IndexHtml(htmlSample3).getBaseHref(), '');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws on invalid baseHref', () {
|
||||||
|
expect(() => IndexHtml('<base href>').getBaseHref(), throwsToolExit());
|
||||||
|
expect(() => IndexHtml('<base href="">').getBaseHref(), throwsToolExit());
|
||||||
|
expect(() => IndexHtml('<base href="foo/111">').getBaseHref(), throwsToolExit());
|
||||||
|
expect(
|
||||||
|
() => IndexHtml('<base href="foo/111/">').getBaseHref(),
|
||||||
|
throwsToolExit(),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
() => IndexHtml('<base href="/foo/111">').getBaseHref(),
|
||||||
|
throwsToolExit(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('applies substitutions', () {
|
||||||
|
final IndexHtml indexHtml = IndexHtml(htmlSample2);
|
||||||
|
indexHtml.applySubstitutions(
|
||||||
|
baseHref: '/foo/333/',
|
||||||
|
serviceWorkerVersion: 'v123xyz',
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
indexHtml.content,
|
||||||
|
htmlSample2Replaced(
|
||||||
|
baseHref: '/foo/333/',
|
||||||
|
serviceWorkerVersion: 'v123xyz',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('re-parses after substitutions', () {
|
||||||
|
final IndexHtml indexHtml = IndexHtml(htmlSample2);
|
||||||
|
expect(indexHtml.getBaseHref(), ''); // Placeholder base href.
|
||||||
|
|
||||||
|
indexHtml.applySubstitutions(
|
||||||
|
baseHref: '/foo/333/',
|
||||||
|
serviceWorkerVersion: 'v123xyz',
|
||||||
|
);
|
||||||
|
// The parsed base href should be updated after substitutions.
|
||||||
|
expect(indexHtml.getBaseHref(), 'foo/333');
|
||||||
|
});
|
||||||
|
}
|
@ -13,11 +13,11 @@ import 'package:flutter_tools/src/base/logger.dart';
|
|||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
|
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/web.dart';
|
|
||||||
import 'package:flutter_tools/src/compile.dart';
|
import 'package:flutter_tools/src/compile.dart';
|
||||||
import 'package:flutter_tools/src/convert.dart';
|
import 'package:flutter_tools/src/convert.dart';
|
||||||
import 'package:flutter_tools/src/devfs.dart';
|
import 'package:flutter_tools/src/devfs.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:flutter_tools/src/html_utils.dart';
|
||||||
import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
import 'package:flutter_tools/src/isolated/devfs_web.dart';
|
||||||
import 'package:flutter_tools/src/web/compile.dart';
|
import 'package:flutter_tools/src/web/compile.dart';
|
||||||
import 'package:logging/logging.dart' as logging;
|
import 'package:logging/logging.dart' as logging;
|
||||||
@ -73,7 +73,6 @@ void main() {
|
|||||||
flutterRoot: null, // ignore: avoid_redundant_argument_values
|
flutterRoot: null, // ignore: avoid_redundant_argument_values
|
||||||
platform: FakePlatform(),
|
platform: FakePlatform(),
|
||||||
webBuildDirectory: null, // ignore: avoid_redundant_argument_values
|
webBuildDirectory: null, // ignore: avoid_redundant_argument_values
|
||||||
basePath: null,
|
|
||||||
);
|
);
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
Logger: () => logger,
|
Logger: () => logger,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user