// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:convert'; import 'package:dwds/src/debugging/dart_runtime_debugger.dart'; import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/loaders/strategy.dart'; import 'package:dwds/src/readers/asset_reader.dart'; import 'package:dwds/src/services/expression_compiler.dart'; import 'package:path/path.dart' as p; import 'package:shelf/shelf.dart'; String removeJsExtension(String path) => path.endsWith('.js') ? p.withoutExtension(path) : path; String addJsExtension(String path) => '$path.js'; /// JavaScript snippet to determine the base URL of the current path. const 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 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 "/"; }()); '''; /// A load strategy for the DDC module system. class DdcStrategy extends LoadStrategy { @override final ReloadConfiguration reloadConfiguration; /// Returns a map of module name to corresponding server path (excluding .js) /// for the provided Dart application entrypoint. /// /// For example: /// /// web/main -> main.ddc /// packages/path/path -> packages/path/path.ddc /// final Future> Function(MetadataProvider metadataProvider) _moduleProvider; /// Returns a map of module name to corresponding digest value. /// /// For example: /// /// web/main -> 8363b363f74b41cac955024ab8b94a3f /// packages/path/path -> d348c2a4647e998011fe305f74f22961 /// final Future> Function(MetadataProvider metadataProvider) // ignore: unused_field _digestsProvider; /// Returns the module for the corresponding server path. /// /// For example: /// /// /packages/path/path.ddc.js -> packages/path/path /// final Future Function( MetadataProvider metadataProvider, String sourcePath, ) _moduleForServerPath; /// Returns a map from module id to module info. /// /// For example: /// /// web/main -> {main.ddc.full.dill, main.ddc.dill} /// final Future> Function( MetadataProvider metadataProvider, ) _moduleInfoForProvider; /// Returns the server path for the provided module. /// /// For example: /// /// web/main -> main.ddc.js /// final Future Function( MetadataProvider metadataProvider, String module, ) _serverPathForModule; /// Returns the source map path for the provided module. /// /// For example: /// /// web/main -> main.ddc.js.map /// final Future Function( MetadataProvider metadataProvider, String module, ) _sourceMapPathForModule; /// Returns the server path for the app uri. /// /// For example: /// /// org-dartlang-app://web/main.dart -> main.dart /// /// Will return `null` if the provided uri is not /// an app URI. final String? Function(String appUri) _serverPathForAppUri; /// Returns the relative path in google3, determined by the [absolutePath]. /// /// Returns `null` if not a google3 app. final String? Function(String absolutePath) _g3RelativePath; final BuildSettings _buildSettings; DdcStrategy( this.reloadConfiguration, this._moduleProvider, this._digestsProvider, this._moduleForServerPath, this._serverPathForModule, this._sourceMapPathForModule, this._serverPathForAppUri, this._moduleInfoForProvider, AssetReader assetReader, this._buildSettings, this._g3RelativePath, { String? packageConfigPath, }) : super(assetReader, packageConfigPath: packageConfigPath); @override Handler get handler => (request) async { // TODO(markzipan): Implement a hot restarter that uses digests for // the DDC module system. return Response.notFound(request.url.toString()); }; @override String get id => 'ddc'; @override String get moduleFormat => 'ddc'; @override String get loadLibrariesModule => 'ddc_module_loader.ddk.js'; @override String get loadModuleSnippet => 'dart_library.import'; @override late final DartRuntimeDebugger dartRuntimeDebugger = DartRuntimeDebugger( loadStrategy: this, useLibraryBundleExpression: false, ); @override BuildSettings get buildSettings => _buildSettings; @override Future bootstrapFor(String entrypoint) async => await _ddcLoaderSetup(entrypoint); @override String loadClientSnippet(String clientScript) => 'window.\$dartLoader.forceLoadModule("$clientScript");\n'; Future _ddcLoaderSetup(String entrypoint) async { final metadataProvider = metadataProviderFor(entrypoint); final modulePaths = await _moduleProvider(metadataProvider); final scripts = >[]; modulePaths.forEach((name, path) { scripts.add({'src': '$path.js', 'id': name}); }); return ''' $baseUrlScript var scripts = ${const JsonEncoder.withIndent(" ").convert(scripts)}; window.\$dartLoader.loadConfig.loadScriptFn = function(loader) { loader.addScriptsToQueue(scripts, null); loader.loadEnqueuedModules(); }; window.\$dartLoader.loader.nextAttempt(); '''; } @override Future moduleForServerPath(String entrypoint, String serverPath) => _moduleForServerPath(metadataProviderFor(entrypoint), serverPath); @override Future> moduleInfoForEntrypoint(String entrypoint) => _moduleInfoForProvider(metadataProviderFor(entrypoint)); @override Future serverPathForModule(String entrypoint, String module) => _serverPathForModule(metadataProviderFor(entrypoint), module); @override Future sourceMapPathForModule(String entrypoint, String module) => _sourceMapPathForModule(metadataProviderFor(entrypoint), module); @override String? serverPathForAppUri(String appUri) => _serverPathForAppUri(appUri); @override String? g3RelativePath(String absolutePath) => _g3RelativePath(absolutePath); }