| // 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: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] |
| /// loads the special Dart stack trace mapper. The [entrypoint] is the |
| /// actual main.dart file. |
| /// |
| /// This file is served when the browser requests "main.dart.js" in debug mode, |
| /// and is responsible for bootstrapping the RequireJS modules and attaching |
| /// the hot reload hooks. |
| String generateBootstrapScript({ |
| @required String requireUrl, |
| @required String mapperUrl, |
| @required String entrypoint, |
| }) { |
| return ''' |
| "use strict"; |
| |
| // Attach source mapping. |
| var mapperEl = document.createElement("script"); |
| mapperEl.defer = true; |
| mapperEl.async = false; |
| mapperEl.src = "$mapperUrl"; |
| document.head.appendChild(mapperEl); |
| |
| // Attach require JS. |
| var requireEl = document.createElement("script"); |
| 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.bootstrap"); |
| document.head.appendChild(requireEl); |
| |
| // Invoked by connected chrome debugger for hot reload/restart support. |
| window.\$hotReloadHook = function(modules) { |
| return new Promise(function(resolve, reject) { |
| if (modules == null) { |
| reject(); |
| } |
| // If no modules change, return immediately. |
| if (modules.length == 0) { |
| resolve(); |
| } |
| var reloadCount = 0; |
| for (var i = 0; i < modules.length; i++) { |
| require.undef(modules[i]); |
| require([modules[i]], function(module) { |
| reloadCount += 1; |
| // once we've reloaded every module, trigger the hot reload. |
| if (reloadCount == modules.length) { |
| require(["$entrypoint", "dart_sdk"], function(app, dart_sdk) { |
| // See the doc comment under in generateMainModule. |
| window.\$dartRunMain = app[Object.keys(app)[0]].main; |
| window.\$hotReload(resolve); |
| }); |
| } |
| }); |
| } |
| }); |
| } |
| '''; |
| } |
| |
| /// 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 '''/* 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.bootstrap", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) { |
| dart_sdk.dart.setStartAsyncSynchronously(true); |
| dart_sdk._debugger.registerDevtoolsFormatter(); |
| \$dartLoader.getModuleLibraries = dart_sdk.dart.getModuleLibraries; |
| if (window.\$dartStackTraceUtility && !window.\$dartStackTraceUtility.ready) { |
| window.\$dartStackTraceUtility.ready = true; |
| let dart = dart_sdk.dart; |
| window.\$dartStackTraceUtility.setSourceMapProvider( |
| function(url) { |
| url = url.replace(baseUrl, '/'); |
| var module = window.\$dartLoader.urlToModuleId.get(url); |
| if (!module) { |
| if (url.endsWith('dart_sdk.js')) { |
| module = 'dart_sdk'; |
| } else { |
| try { |
| module = '/packages' + url.split('packages')[1].split('.')[0] + '.dart'; |
| } catch (e) { |
| return null; |
| } |
| } |
| } |
| return dart.getSourceMap(module); |
| }); |
| } |
| if (typeof document != 'undefined') { |
| window.postMessage({ type: "DDC_STATE_CHANGE", state: "start" }, "*"); |
| } |
| dart_sdk._isolate_helper.startRootIsolate(() => {}, []); |
| |
| // 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.\$dartRunMain(); |
| window.requestAnimationFrame(cb); |
| }); |
| } |
| } |
| |
| /* MAIN_EXTENSION_MARKER */ |
| child.main(); |
| }); |
| |
| // Require JS configuration. |
| require.config({ |
| waitSeconds: 0, |
| }); |
| '''; |
| } |