Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion source/npm/qsharp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
"directory": "npm"
},
"exports": {
".": "./dist/main.js",
".": {
"browser": "./dist/browser.js",
"node": "./dist/node.js",
"default": "./dist/browser.js"
},
"./compiler-worker": "./dist/compiler/worker.js",
"./language-service-worker": "./dist/language-service/worker.js",
"./debug-service-worker": "./dist/debug-service/worker.js",
Expand Down
6 changes: 6 additions & 0 deletions source/npm/qsharp/src/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Browser entrypoint. No Worker polyfill needed — browsers provide it natively.

export * from "./main.js";
Comment thread
minestarks marked this conversation as resolved.
Outdated
14 changes: 10 additions & 4 deletions source/npm/qsharp/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// This module is the single entry point for both browser and Node.js environments.
// Shared implementation. Use the browser or node entrypoint instead of importing directly.

import * as wasm from "../lib/web/qsc_wasm.js";
import initWasm, {
Expand Down Expand Up @@ -32,6 +32,12 @@ import { log } from "./log.js";
import { ProjectLoader } from "./project.js";
import { createProxy } from "./workers/main.js";

let workerType: "classic" | "module" = "classic";

export function setWorkerType(type: "classic" | "module") {
workerType = type;
}

// Create once. A module is stateless and can be efficiently passed to WebWorkers.
let wasmModule: WebAssembly.Module | null = null;
let wasmModulePromise: Promise<void> | null = null;
Expand Down Expand Up @@ -126,7 +132,7 @@ export function getDebugServiceWorker(
worker: string | Worker,
): IDebugServiceWorker {
if (!wasmModule) throw "Wasm module must be loaded first";
return createProxy(worker, wasmModule, debugServiceProtocol);
return createProxy(worker, wasmModule, debugServiceProtocol, workerType);
}

export async function getCompiler(): Promise<ICompiler> {
Expand All @@ -139,7 +145,7 @@ export async function getCompiler(): Promise<ICompiler> {
// messages, then the worker may be passed in and it will be initialized.
export function getCompilerWorker(worker: string | Worker): ICompilerWorker {
if (!wasmModule) throw "Wasm module must be loaded first";
return createProxy(worker, wasmModule, compilerProtocol);
return createProxy(worker, wasmModule, compilerProtocol, workerType);
}

export async function getLanguageService(
Expand All @@ -156,7 +162,7 @@ export function getLanguageServiceWorker(
worker: string | Worker,
): ILanguageServiceWorker {
if (!wasmModule) throw "Wasm module must be loaded first";
return createProxy(worker, wasmModule, languageServiceProtocol);
return createProxy(worker, wasmModule, languageServiceProtocol, workerType);
}

/// Extracts the target profile from a Q# source file's entry point.
Expand Down
14 changes: 14 additions & 0 deletions source/npm/qsharp/src/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Node.js entrypoint. Polyfills the Worker global before loading the main module.

import worker from "web-worker";
import { setWorkerType } from "./main.js";

if (typeof globalThis.Worker === "undefined") {
globalThis.Worker = worker;
}
Comment thread
joao-boechat marked this conversation as resolved.
setWorkerType("module");

export * from "./main.js";
25 changes: 3 additions & 22 deletions source/npm/qsharp/src/workers/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,14 @@ import type {
ServiceState,
} from "./types.js";

export const isBrowser = typeof Worker !== "undefined";

if (!isBrowser) {
// In CJS (esbuild bundle), require is available directly.
// In ESM (e.g. node --test), we use dynamic import.
if (typeof require === "function") {
// eslint-disable-next-line @typescript-eslint/no-require-imports
globalThis.Worker = require("web-worker");
} else {
// Dynamic import for ESM - this is lazy, Worker will be available
// by the time it's actually needed.
import("web-worker").then((mod) => {
globalThis.Worker = mod.default;
});
}
log.debug(
"Running in Node.js environment, using web-worker package for Worker support.",
);
}
/**
* Creates and initializes a service in a web worker, and returns a proxy for the service
* to be used from the main thread.
*
* @param workerArg The service web worker or the URL of the web worker script.
* @param wasmModule The wasm module to initialize the service with
* @param serviceProtocol An object that describes the service: its constructor, methods and events
* @param workerType The type of worker to create: "classic" for browsers, "module" for Node.js
* @returns A proxy object that implements the service interface.
* This interface can now be used as if calling into the real service,
* and the calls will be proxied to the web worker.
Expand All @@ -56,11 +38,10 @@ export function createProxy<
workerArg: string | Worker,
wasmModule: WebAssembly.Module,
serviceProtocol: ServiceProtocol<TService, TServiceEventMsg>,
workerType: "classic" | "module",
): TService & IServiceProxy {
// Create or use the WebWorker
const useModuleWorker: WorkerOptions = isBrowser
? { type: "classic" }
: { type: "module" };
const useModuleWorker: WorkerOptions = { type: workerType };

const worker =
typeof workerArg === "string"
Expand Down
2 changes: 1 addition & 1 deletion source/npm/qsharp/test/basics.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
getDebugServiceWorker,
loadWasmModule,
utils,
} from "../dist/main.js";
} from "../dist/node.js";

import { QscEventTarget } from "../dist/compiler/events.js";
import { getAllKatas, getExerciseSources, getKata } from "../dist/katas.js";
Expand Down
2 changes: 1 addition & 1 deletion source/npm/qsharp/test/circuits.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { afterEach, beforeEach, test } from "node:test";
import { fileURLToPath } from "node:url";
import prettier from "prettier";
import { log } from "../dist/log.js";
import { getCompiler, loadWasmModule } from "../dist/main.js";
import { getCompiler, loadWasmModule } from "../dist/node.js";
import { draw } from "../dist/ux/circuit-vis/index.js";

// Load the wasm module before running any tests
Expand Down
2 changes: 1 addition & 1 deletion source/npm/qsharp/test/diagnostics.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
getCompilerWorker,
getProjectLoader,
loadWasmModule,
} from "../dist/main.js";
} from "../dist/node.js";

const distDir = new URL("../dist/", import.meta.url);
const compilerWorkerPath = new URL("compiler/worker.js", distDir).href;
Expand Down
2 changes: 1 addition & 1 deletion source/npm/qsharp/test/languageService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { test } from "node:test";
import { log } from "../dist/log.js";
import { getLanguageService, loadWasmModule } from "../dist/main.js";
import { getLanguageService, loadWasmModule } from "../dist/node.js";

// Load the wasm module before running any tests
const wasmPath = new URL("../lib/web/qsc_wasm_bg.wasm", import.meta.url);
Expand Down
Loading