// 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 metadataFiles = {}; final files = {}; final sourcemaps = {}; String? get mergedMetadata => _mergedMetadata; String? _mergedMetadata; /// Update the filesystem with the provided source and manifest files. /// /// Returns the list of updated files. List write(File codeFile, File manifestFile, File sourcemapFile, File metadataFile) { final modules = []; final Uint8List codeBytes = codeFile.readAsBytesSync(); final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync(); final Uint8List metadataBytes = metadataFile.readAsBytesSync(); final Map manifest = castStringKeyedMap( json.decode(manifestFile.readAsStringSync()), )!; for (final String filePath in manifest.keys) { final Map offsets = castStringKeyedMap(manifest[filePath])!; final List codeOffsets = (offsets['code'] as List).cast(); final List sourcemapOffsets = (offsets['sourcemap'] as List).cast(); final List metadataOffsets = (offsets['metadata'] as List).cast(); 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 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 sourcemapView = Uint8List.view( sourcemapBytes.buffer, sourcemapStart, sourcemapEnd - sourcemapStart, ); final sourcemapName = '$fileName.map'; sourcemaps[sourcemapName] = sourcemapView; final int metadataStart = metadataOffsets[0]; final int metadataEnd = metadataOffsets[1]; if (metadataStart < 0 || metadataEnd > metadataBytes.lengthInBytes) { continue; } final metadataView = Uint8List.view( metadataBytes.buffer, metadataStart, metadataEnd - metadataStart, ); final metadataName = '$fileName.metadata'; metadataFiles[metadataName] = metadataView; modules.add(fileName); } _mergedMetadata = metadataFiles.values .map((Uint8List encoded) => utf8.decode(encoded)) .join('\n'); return modules; } }