/*! * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import { DirEnt, FSWatchCallback, FSWatchOptions, FileSystemAPI, FileSystemTree, IFSWatcher, Unsubscribe, WebContainer, WebContainerProcess, } from '@webcontainer/api'; export class FakeEventTarget implements EventTarget { listeners: Map = new Map(); addEventListener(type: string, listener: EventListenerOrEventListenerObject): void { const listeners = this.listeners.get(type) || []; listeners.push(listener); this.listeners.set(type, listeners); } removeEventListener(type: string, listener: EventListenerOrEventListenerObject): void { const listeners = this.listeners.get(type); if (listeners) { const index = listeners.indexOf(listener); if (index !== -1) { listeners.splice(index, 1); } } } dispatchEvent(event: Event): boolean { const listeners = this.listeners.get(event.type); if (listeners) { for (const listener of listeners) { if (typeof listener === 'function') { listener.call(this, event); } else { listener.handleEvent(event); } } } return true; } } export class MockLocalStorage implements Pick { private items = new Map(); getItem(key: string): string | null { return this.items.get(key) ?? null; } setItem(key: string, value: string | null): void { this.items.set(key, value); } } export class FakeWebContainer extends WebContainer { fakeSpawn: FakeWebContainerProcess | undefined = undefined; constructor(fakeOptions?: {spawn: FakeWebContainerProcess}) { super(); if (fakeOptions?.spawn) this.fakeSpawn = fakeOptions.spawn; } override async spawn( command: unknown, args?: unknown, options?: unknown, ): Promise { if (this.fakeSpawn) return this.fakeSpawn; return new FakeWebContainerProcess(); } override on(event: unknown, listener: unknown): Unsubscribe { return () => {}; } override async mount( tree: FileSystemTree, options?: {mountPoint?: string | undefined} | undefined, ): Promise {} override get path() { return '/fake-path'; } override get workdir() { return '/fake-workdir'; } override teardown() {} override fs: FakeFileSystemAPI = new FakeFileSystemAPI(); override async setPreviewScript(script: string): Promise {} } class FakeFileSystemAPI implements FileSystemAPI { readdir( path: string, options: 'buffer' | {encoding: 'buffer'; withFileTypes?: false | undefined}, ): Promise; readdir( path: string, options?: | string | {encoding?: string | null | undefined; withFileTypes?: false | undefined} | null | undefined, ): Promise; readdir( path: string, options: {encoding: 'buffer'; withFileTypes: true}, ): Promise[]>; readdir( path: string, options: {encoding?: string | null | undefined; withFileTypes: true}, ): Promise[]>; async readdir( path: unknown, options?: {encoding?: string | null | undefined; withFileTypes?: boolean} | string | null, ): Promise[] | DirEnt[]> { if (typeof options === 'object' && options?.withFileTypes === true) { return [{name: 'fake-file', isFile: () => true, isDirectory: () => false}]; } return ['/fake-dirname']; } readFile(path: string, encoding?: null | undefined): Promise; readFile(path: string, encoding: string): Promise; readFile(path: unknown, encoding?: unknown): Promise | Promise { return Promise.resolve('fake file content'); } async writeFile( path: string, data: string | Uint8Array, options?: string | {encoding?: string | null | undefined} | null | undefined, ): Promise {} mkdir(path: string, options?: {recursive?: false | undefined} | undefined): Promise; mkdir(path: string, options: {recursive: true}): Promise; async mkdir(path: unknown, options?: unknown): Promise {} async rm( path: string, options?: {force?: boolean | undefined; recursive?: boolean | undefined} | undefined, ): Promise {} rename(oldPath: string, newPath: string): Promise { throw Error('Not implemented'); } watch( filename: string, options?: FSWatchOptions | undefined, listener?: FSWatchCallback | undefined, ): IFSWatcher; watch(filename: string, listener?: FSWatchCallback | undefined): IFSWatcher; watch(filename: unknown, options?: unknown, listener?: unknown): IFSWatcher { throw Error('Not implemented'); } } export class FakeWebContainerProcess implements WebContainerProcess { exit: Promise = Promise.resolve(0); input: WritableStream = new WritableStream(); output: ReadableStream = new ReadableStream(); kill(): void {} resize(dimensions: {cols: number; rows: number}): void {} }