microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
npm/README.md
97lines · modecode
| 1 | # qsharp npm module |
| 2 | |
| 3 | This package contains the qsharp compiler functionality shipped for consumption via npm. |
| 4 | |
| 5 | The source is written in TypeScript, which is compiled to ECMAScript modules in the ./dist directory. |
| 6 | The wasm binaries from the Rust builds are copied to the ./lib directory. |
| 7 | |
| 8 | Consuming browser projects should import from this module and use a bundler to create their |
| 9 | own JavaScript bundle, and also copy the wasm file to their project and provide the URL |
| 10 | to it when calling the `loadWasmModule` method so it may be located and loaded. |
| 11 | |
| 12 | ## Node and browser support |
| 13 | |
| 14 | wasm-pack generates different files for the browser and Node.js environments. The wasm is slightly |
| 15 | different, and the loader code is quite different. This can be seen in `./lib/web/qsc_wasm.cjs` |
| 16 | and `./lib/node/qsc_wasm.js` files respectively. Specifically, the web environment loads the wasm |
| 17 | file using async web APIs such as `fetch` with a URI, and Node.js uses `require` to load the `fs` module |
| 18 | and calls to `readFileSync`. Once the wasm module is loaded however, the exported APIs are used |
| 19 | in a similar manner. |
| 20 | |
| 21 | To support using this npm package from both environments, the package uses "conditional exports" |
| 22 | <https://nodejs.org/dist/latest-v18.x/docs/api/packages.html#conditional-exports> to expose one |
| 23 | entry point for Node.js, and another for browsers. The distinct entry points uses their respective |
| 24 | loader to load the wasm module for the platform, and then expose functionality that uses the |
| 25 | loaded module via common code. |
| 26 | |
| 27 | When bundling for the web, bundlers such as esbuild will automatically use the default entry point, |
| 28 | whereas when loaded as a Node.js module, it will use the "node" entry point. |
| 29 | |
| 30 | Note that TypeScript seems to add the ['import', 'types', 'node'] conditions by default when |
| 31 | searching the Node.js `exports`, and so will always find the 'node' export before the 'default' |
| 32 | export. To resolve this, a 'browser' condition was added (which is same as 'default' but earlier |
| 33 | than 'node') and the tsconfig compiler option `"customConditions": ["browser"]` should be added |
| 34 | (requires TypeScript 5.0 or later). esbuild also adds the 'browser' condition when bundling for |
| 35 | the browser (see <https://esbuild.github.io/api/#how-conditions-work>). |
| 36 | |
| 37 | ## Design |
| 38 | |
| 39 | The API for using this module is similar whether using a browser or Node.js, and whether running |
| 40 | in the main thread or a worker thread. You instantiate the compiler, and call operations on it |
| 41 | which complete in the order called. |
| 42 | |
| 43 | All operations return a Promise which resolves then the operation is complete. Some operations |
| 44 | may also emit events, such as debug messages or state dumps as they are processed. A call may |
| 45 | also be passed a CancellationToken so that if the result is no longer needed then the operation |
| 46 | may be cancelled before starting. |
| 47 | |
| 48 | If the caller is not interested in any interim events or being able to cancel the request, |
| 49 | these arguments are optional. For example: |
| 50 | |
| 51 | ```js |
| 52 | const codeSample = "namespace Test { operation Main() {....} }"; |
| 53 | const entryPoint = "Test.Main()"; |
| 54 | |
| 55 | const compiler = getCompilerWorker(); |
| 56 | const cancelSrc = new CancellationTokenSource(); |
| 57 | const runEvents = new QscEventTarget(false /* store record of events */); |
| 58 | |
| 59 | // Log any DumpMachine calls |
| 60 | runEvents.addEventListener('DumpMachine', (evt) => console.log("DumpMachine: %o", evt.detail)); |
| 61 | |
| 62 | compiler.run(codeSample, entryPoint, 1 /* shots */, runEvents, cancelSrc.token) |
| 63 | .then(result => console.log("Run result: %s", result)); |
| 64 | .catch(err => console.err("Run failed with: %o", err)); |
| 65 | |
| 66 | cancelButton.addEventListener('click', () => { |
| 67 | // Try to cancel the operation if still pending |
| 68 | tokenSource.cancel(); |
| 69 | }); |
| 70 | |
| 71 | // Also run the below request, but don't care about events or cancellation, just the result |
| 72 | const checkResult = await compiler.check(code); |
| 73 | console.log('check result was: %o', checkResult); |
| 74 | ``` |
| 75 | |
| 76 | Promises, Events, and Cancellation are based on JavaScript or Web standards, or the VS Code API: |
| 77 | |
| 78 | - Promises <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises> |
| 79 | - EventTarget <https://developer.mozilla.org/en-US/docs/Web/API/EventTarget> |
| 80 | - Event <https://developer.mozilla.org/en-US/docs/Web/API/Event/Event> |
| 81 | - VS Code API for CancellationToken <https://code.visualstudio.com/api/references/vscode-api#CancellationToken> |
| 82 | |
| 83 | The standard Web APIs for custom events were added to Node.js in v16.17. <https://nodejs.org/dist/v16.17.0/docs/api/events.html>, but behind an experimental flag. As CustomEvent is not on |
| 84 | the global by default until v19 or later, the code will use Event with a 'detail' |
| 85 | property manually set until v20 is in common use. |
| 86 | |
| 87 | The VS Code implementation for cancellation tokens is viewable in their source code |
| 88 | at <src/vs/base/common/cancellation.ts>. This code uses a simplified version of that API. |
| 89 | |
| 90 | ## Testing |
| 91 | |
| 92 | Node.js tests can be run via `node --test` (see |
| 93 | <https://nodejs.org/dist/latest-v18.x/docs/api/test.html#test-runner-execution-model>). |
| 94 | |
| 95 | The test module was also added to Node.js v16.17.0, and Electron 22 (which VS Code plans to move to |
| 96 | in first half of 2023) includes v16.17.1, so v16.17 should be our minimum Node.js |
| 97 | version supported (it shipped in Aug 2022). |
| 98 | |