diff --git a/adev/src/app/app.config.ts b/adev/src/app/app.config.ts index 1bc9b4fcf86..bd370768a87 100644 --- a/adev/src/app/app.config.ts +++ b/adev/src/app/app.config.ts @@ -32,6 +32,7 @@ import {ContentLoader} from './core/services/content-loader.service'; import {CustomErrorHandler} from './core/services/errors-handling/error-handler'; import {ExampleContentLoader} from './core/services/example-content-loader.service'; import {routerProviders} from './routing/router_providers'; +import {TYPESCRIPT_VFS_WORKER_PROVIDER} from './editor/code-editor/workers/factory-provider'; export const appConfig: ApplicationConfig = { providers: [ @@ -51,5 +52,6 @@ export const appConfig: ApplicationConfig = { useFactory: (document: Document) => windowProvider(document), deps: [DOCUMENT], }, + TYPESCRIPT_VFS_WORKER_PROVIDER, ], }; diff --git a/adev/src/app/editor/code-editor/code-mirror-editor.service.spec.ts b/adev/src/app/editor/code-editor/code-mirror-editor.service.spec.ts index 2e58dcb06e5..d109e21e717 100644 --- a/adev/src/app/editor/code-editor/code-mirror-editor.service.spec.ts +++ b/adev/src/app/editor/code-editor/code-mirror-editor.service.spec.ts @@ -17,6 +17,7 @@ import {EmbeddedTutorialManager} from '../embedded-tutorial-manager.service'; import {CodeMirrorEditor, EDITOR_CONTENT_CHANGE_DELAY_MILLIES} from './code-mirror-editor.service'; import {TutorialConfig, TutorialMetadata} from '@angular/docs'; +import {TYPESCRIPT_VFS_WORKER_FACTORY} from './workers/factory-provider'; class FakeNodeRuntimeSandbox { async writeFile(path: string, content: string) {} @@ -64,8 +65,16 @@ export class FakeEmbeddedTutorialManager { } } +class MockWorker { + addEventListener = jasmine.createSpy('addEventListener'); + postMessage = jasmine.createSpy('postMessage'); + removeEventListener = jasmine.createSpy('removeEventListener'); + terminate = jasmine.createSpy('terminate'); +} + describe('CodeMirrorEditor', () => { let service: CodeMirrorEditor; + let mockWorker: MockWorker; const fakeNodeRuntimeSandbox = new FakeNodeRuntimeSandbox(); const fakeEmbeddedTutorialManager = new FakeEmbeddedTutorialManager(); @@ -80,6 +89,8 @@ describe('CodeMirrorEditor', () => { } beforeEach(() => { + mockWorker = new MockWorker(); + TestBed.configureTestingModule({ providers: [ CodeMirrorEditor, @@ -87,6 +98,10 @@ describe('CodeMirrorEditor', () => { provide: NodeRuntimeSandbox, useValue: fakeNodeRuntimeSandbox, }, + { + provide: TYPESCRIPT_VFS_WORKER_FACTORY, + useValue: () => mockWorker as unknown as Worker, + }, { provide: EmbeddedTutorialManager, useValue: fakeEmbeddedTutorialManager, diff --git a/adev/src/app/editor/code-editor/code-mirror-editor.service.ts b/adev/src/app/editor/code-editor/code-mirror-editor.service.ts index 4db25c8654c..a1856e3f9db 100644 --- a/adev/src/app/editor/code-editor/code-mirror-editor.service.ts +++ b/adev/src/app/editor/code-editor/code-mirror-editor.service.ts @@ -29,6 +29,7 @@ import {TsVfsWorkerActions} from './workers/enums/actions'; import {CodeChangeRequest} from './workers/interfaces/code-change-request'; import {ActionMessage} from './workers/interfaces/message'; import {NodeRuntimeState} from '../node-runtime-state.service'; +import {TYPESCRIPT_VFS_WORKER_FACTORY} from './workers/factory-provider'; export interface EditorFile { filename: string; @@ -71,7 +72,6 @@ export class CodeMirrorEditor { // An instance of web worker used to run virtual TypeScript environment in the browser. // It allows to enrich CodeMirror UX for TypeScript files. - private tsVfsWorker: Worker | null = null; // EventManager gives ability to communicate between tsVfsWorker and CodeMirror instance private readonly eventManager$ = new Subject(); @@ -81,6 +81,8 @@ export class CodeMirrorEditor { private readonly typingsLoader = inject(TypingsLoader); private readonly destroyRef = inject(DestroyRef); private readonly diagnosticsState = inject(DiagnosticsState); + private readonly tsVfsWorkerFactory = inject(TYPESCRIPT_VFS_WORKER_FACTORY); + private tsVfsWorker: Worker | null = null; private _editorView: EditorView | null = INITIAL_STATES._editorView; private readonly _editorStates = new Map(); @@ -182,14 +184,11 @@ export class CodeMirrorEditor { } private initTypescriptVfsWorker(): void { - if (this.tsVfsWorker) { + if (this.tsVfsWorker || !this.tsVfsWorkerFactory) { return; } - this.tsVfsWorker = new Worker(new URL('./workers/typescript-vfs.worker', import.meta.url), { - type: 'module', - }); - + this.tsVfsWorker = this.tsVfsWorkerFactory(); this.tsVfsWorker.addEventListener('message', ({data}: MessageEvent) => { this.eventManager$.next(data); }); @@ -214,10 +213,13 @@ export class CodeMirrorEditor { // Method is responsible for sending request to Typescript VFS worker. private sendRequestToTsVfs = (request: ActionMessage) => { - if (!this.tsVfsWorker) return; + if (!this.tsVfsWorker) { + console.warn('TypeScript VFS worker not available'); + return; + } // Send message to tsVfsWorker only when current file is TypeScript file. - if (!this.currentFile().filename.endsWith('.ts')) return; + if (!this.currentFile()?.filename.endsWith('.ts')) return; this.tsVfsWorker.postMessage(request); }; diff --git a/adev/src/app/editor/code-editor/workers/factory-provider.ts b/adev/src/app/editor/code-editor/workers/factory-provider.ts new file mode 100644 index 00000000000..072c833d087 --- /dev/null +++ b/adev/src/app/editor/code-editor/workers/factory-provider.ts @@ -0,0 +1,26 @@ +/*! + * @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 {InjectionToken, Provider} from '@angular/core'; + +export type TypescriptVfsWorkerFactory = () => Worker; + +export const TYPESCRIPT_VFS_WORKER_FACTORY = new InjectionToken( + 'TYPESCRIPT_VFS_WORKER_FACTORY', +); + +export function createTypescriptVfsWorker(): Worker { + return new Worker(new URL('./typescript-vfs.worker.ts', import.meta.url), { + type: 'module', + }); +} + +export const TYPESCRIPT_VFS_WORKER_PROVIDER: Provider = { + provide: TYPESCRIPT_VFS_WORKER_FACTORY, + useValue: createTypescriptVfsWorker, +};