| // 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 'dart:typed_data'; |
| |
| import '../base/file_system.dart'; |
| import '../base/utils.dart'; |
| import '../convert.dart'; |
| |
| /// A pseudo-filesystem stored in memory. |
| /// |
| /// To support output to arbitrary multi-root file schemes, the frontend server |
| /// will output web sources, sourcemaps, and metadata to concatenated single files |
| /// with an additional manifest file containing the correct offsets. |
| class WebMemoryFS { |
| final Map<String, Uint8List> metadataFiles = <String, Uint8List>{}; |
| final Map<String, Uint8List> files = <String, Uint8List>{}; |
| final Map<String, Uint8List> sourcemaps = <String, Uint8List>{}; |
| |
| String? get mergedMetadata => _mergedMetadata; |
| String? _mergedMetadata; |
| |
| /// Update the filesystem with the provided source and manifest files. |
| /// |
| /// Returns the list of updated files. |
| List<String> write( |
| File codeFile, |
| File manifestFile, |
| File sourcemapFile, |
| File metadataFile, |
| ) { |
| final List<String> modules = <String>[]; |
| final Uint8List codeBytes = codeFile.readAsBytesSync(); |
| final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync(); |
| final Uint8List metadataBytes = metadataFile.readAsBytesSync(); |
| final Map<String, dynamic> manifest = |
| castStringKeyedMap(json.decode(manifestFile.readAsStringSync()))!; |
| for (final String filePath in manifest.keys) { |
| if (filePath == null) { |
| continue; |
| } |
| final Map<String, dynamic> offsets = |
| castStringKeyedMap(manifest[filePath])!; |
| final List<int> codeOffsets = |
| (offsets['code'] as List<dynamic>).cast<int>(); |
| final List<int> sourcemapOffsets = |
| (offsets['sourcemap'] as List<dynamic>).cast<int>(); |
| final List<int> metadataOffsets = |
| (offsets['metadata'] as List<dynamic>).cast<int>(); |
| if (codeOffsets.length != 2 || |
| sourcemapOffsets.length != 2 || |
| metadataOffsets.length != 2) { |
| continue; |
| } |
| |
| final int codeStart = codeOffsets[0]; |
| final int codeEnd = codeOffsets[1]; |
| if (codeStart < 0 || codeEnd > codeBytes.lengthInBytes) { |
| continue; |
| } |
| final Uint8List byteView = Uint8List.view( |
| codeBytes.buffer, |
| codeStart, |
| codeEnd - codeStart, |
| ); |
| final String fileName = |
| filePath.startsWith('/') ? filePath.substring(1) : filePath; |
| files[fileName] = byteView; |
| |
| final int sourcemapStart = sourcemapOffsets[0]; |
| final int sourcemapEnd = sourcemapOffsets[1]; |
| if (sourcemapStart < 0 || sourcemapEnd > sourcemapBytes.lengthInBytes) { |
| continue; |
| } |
| final Uint8List sourcemapView = Uint8List.view( |
| sourcemapBytes.buffer, |
| sourcemapStart, |
| sourcemapEnd - sourcemapStart, |
| ); |
| final String sourcemapName = '$fileName.map'; |
| sourcemaps[sourcemapName] = sourcemapView; |
| |
| final int metadataStart = metadataOffsets[0]; |
| final int metadataEnd = metadataOffsets[1]; |
| if (metadataStart < 0 || metadataEnd > metadataBytes.lengthInBytes) { |
| continue; |
| } |
| final Uint8List metadataView = Uint8List.view( |
| metadataBytes.buffer, |
| metadataStart, |
| metadataEnd - metadataStart, |
| ); |
| final String metadataName = '$fileName.metadata'; |
| metadataFiles[metadataName] = metadataView; |
| |
| modules.add(fileName); |
| } |
| |
| _mergedMetadata = metadataFiles.values |
| .map((Uint8List encoded) => utf8.decode(encoded)) |
| .join('\n'); |
| |
| return modules; |
| } |
| } |