From 0c1176d9130afbc9a702d088388aa6e81f5d852e Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Wed, 20 Mar 2024 12:41:03 -0400 Subject: [PATCH 1/4] feat(deno): Publish to JSR --- .changeset/sweet-glasses-battle.md | 7 +++++++ .github/workflows/jsr.yml | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .changeset/sweet-glasses-battle.md create mode 100644 .github/workflows/jsr.yml diff --git a/.changeset/sweet-glasses-battle.md b/.changeset/sweet-glasses-battle.md new file mode 100644 index 00000000..100b226c --- /dev/null +++ b/.changeset/sweet-glasses-battle.md @@ -0,0 +1,7 @@ +--- +"@anywidget/deno": patch +--- + +Publish package to [`jsr:@anywidget/deno`](https://jsr.io/@anywidget/deno) + +Deprecates https://deno.land/x/anywidget distribution for the preferred [JSR](https://jsr.io). diff --git a/.github/workflows/jsr.yml b/.github/workflows/jsr.yml new file mode 100644 index 00000000..708765ca --- /dev/null +++ b/.github/workflows/jsr.yml @@ -0,0 +1,21 @@ +name: Publish @anywidget/deno to jsr + +on: + push: + branches: + - main + +jobs: + publish: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v4 + - name: Publish package + run: | + cd packages/deno + npx jsr publish From e0a6855406b6f931f95495882aea8c70f3787300 Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Wed, 20 Mar 2024 12:42:59 -0400 Subject: [PATCH 2/4] module docs --- packages/deno/src/install.ts | 22 +++++++++++----------- packages/deno/src/mod.ts | 9 ++++++--- packages/deno/src/uninstall.ts | 6 +++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/deno/src/install.ts b/packages/deno/src/install.ts index aeff2635..c228127b 100644 --- a/packages/deno/src/install.ts +++ b/packages/deno/src/install.ts @@ -1,14 +1,3 @@ -import * as path from "@std/path"; -import * as fs from "@std/fs"; -import * as cli from "@std/cli"; -import * as unzipit from "unzipit"; -import * as z from "zod"; -import { - find_data_dir, - system_data_dirs, - user_data_dir, -} from "./jupyter_paths.ts"; - /** * @module * Install the front-end anywidget assets for JupyterLab. @@ -20,6 +9,17 @@ import { * ``` */ +import * as path from "@std/path"; +import * as fs from "@std/fs"; +import * as cli from "@std/cli"; +import * as unzipit from "unzipit"; +import * as z from "zod"; +import { + find_data_dir, + system_data_dirs, + user_data_dir, +} from "./jupyter_paths.ts"; + let ReleaseSchema = z.object({ packagetype: z.string(), url: z.string(), diff --git a/packages/deno/src/mod.ts b/packages/deno/src/mod.ts index 66cffffa..8a3735c1 100644 --- a/packages/deno/src/mod.ts +++ b/packages/deno/src/mod.ts @@ -1,11 +1,11 @@ -import * as path from "@std/path"; -import { find_data_dir } from "./jupyter_paths.ts"; - /** * Jupyter widgets for the Deno Jupyter kernel. * @module */ +import * as path from "@std/path"; +import { find_data_dir } from "./jupyter_paths.ts"; + let COMMS = new WeakMap(); // TODO: We need to get this version from somewhere. Needs to match packages/anywidget/package.json#version let DEFAULT_VERSION: string = "0.9.3"; @@ -187,6 +187,9 @@ export type FrontEndModel = _Model & { type HTMLElement = typeof globalThis extends { HTMLElement: infer T } ? T : unknown; +/** + * + */ export type WidgetProps = { /** The initial state of the widget. */ state: State; diff --git a/packages/deno/src/uninstall.ts b/packages/deno/src/uninstall.ts index b54072a8..902eb8c3 100644 --- a/packages/deno/src/uninstall.ts +++ b/packages/deno/src/uninstall.ts @@ -1,6 +1,3 @@ -import * as path from "@std/path"; -import { find_data_dir } from "./jupyter_paths.ts"; - /** * @module * @@ -11,6 +8,9 @@ import { find_data_dir } from "./jupyter_paths.ts"; * ``` */ +import * as path from "@std/path"; +import { find_data_dir } from "./jupyter_paths.ts"; + let data_dir = await find_data_dir(); await Deno.readTextFile( From fa7b23b4e64e66999e92c5971359b3d7990f28fe Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Wed, 20 Mar 2024 12:52:55 -0400 Subject: [PATCH 3/4] formatting --- .changeset/sweet-glasses-battle.md | 3 +- packages/deno/src/mod.ts | 71 ++++++++++++++++++++---------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/.changeset/sweet-glasses-battle.md b/.changeset/sweet-glasses-battle.md index 100b226c..9682a85a 100644 --- a/.changeset/sweet-glasses-battle.md +++ b/.changeset/sweet-glasses-battle.md @@ -4,4 +4,5 @@ Publish package to [`jsr:@anywidget/deno`](https://jsr.io/@anywidget/deno) -Deprecates https://deno.land/x/anywidget distribution for the preferred [JSR](https://jsr.io). +Deprecates https://deno.land/x/anywidget distribution for the preferred +[JSR](https://jsr.io). diff --git a/packages/deno/src/mod.ts b/packages/deno/src/mod.ts index 8a3735c1..08de3f34 100644 --- a/packages/deno/src/mod.ts +++ b/packages/deno/src/mod.ts @@ -62,7 +62,7 @@ interface TestingInternals { /** Get the comm for a model */ get_comm(model: object): Comm; /** Get the init promise for a model */ - get_init_promise(model: _Model): Promise | undefined; + get_init_promise(model: Model): Promise | undefined; /** The version of anywidget used. */ version: string; } @@ -77,8 +77,8 @@ export const _internals: TestingInternals = { } return comm; }, - get_init_promise(model: _Model): Promise | undefined { - // @ts-expect-error - we hide the symbol from the user + get_init_promise(model: Model): Promise | undefined { + // @ts-expect-error - We have tagged this symbol onto the model privately return model[init_promise_symbol]; }, get version() { @@ -99,10 +99,12 @@ class Comm { this.#protocol_version_minor = 1; } + /** The id of the comm. */ get id(): string { return this.#id; } + /** Send a message to the front end to initialize the widget. */ init(): Promise { return _internals.jupyter_broadcast( "comm_open", @@ -130,6 +132,7 @@ class Comm { ); } + /** Send a state update to the front end. */ send_state(state: object): Promise { return _internals.jupyter_broadcast("comm_msg", { comm_id: this.id, @@ -137,6 +140,7 @@ class Comm { }); } + /** The Jupyter "mimebundle" for displaying the underlying widget. */ mimebundle(): Mimebundle { return { "application/vnd.jupyter.widget-view+json": { @@ -152,7 +156,8 @@ type ChangeEvents = { [K in string & keyof State as `change:${K}`]: State[K]; }; -class _Model { +/** A BackboneJS-like model for the anywidget. */ +export class Model { private _state: State; private _target: EventTarget; @@ -160,15 +165,37 @@ class _Model { this._state = state; this._target = new EventTarget(); } + + /** + * Get a property of the state object. + * + * @param key - The property to get. + */ get(key: K): State[K] { return this._state[key]; } + + /** + * Set a property of the state object. + * + * @param key - The property to set. + * @param value - The new value. + */ set(key: K, value: State[K]): void { this._state[key] = value; this._target.dispatchEvent( new CustomEvent(`change:${key as string}`, { detail: value }), ); } + + /** + * Subscribe to changes in the state object. + * + * Note: Only `change:${key}` events are supported. + * + * @param name - The event name to subscribe to. + * @param callback - The callback to call when the event is dispatched. + */ on>( name: Event, callback: () => void, @@ -177,9 +204,9 @@ class _Model { } } -export type Model = typeof _Model; - -export type FrontEndModel = _Model & { +/** The front end variant of the model. */ +export type FrontEndModel = Model & { + /** Sync changes with the Deno kernel. */ save_changes(): void; }; @@ -187,10 +214,16 @@ export type FrontEndModel = _Model & { type HTMLElement = typeof globalThis extends { HTMLElement: infer T } ? T : unknown; -/** - * - */ -export type WidgetProps = { +// TODO: more robust serialization of render function (with context?) +function to_esm({ + imports = "", + render, +}: Pick, "imports" | "render">) { + return `${imports}\nexport default { render: ${render.toString()} }`; +} + +/** The options bag to pass to the {@link widget} method. */ +export type WidgetOptions = { /** The initial state of the widget. */ state: State; /** A function that renders the widget. This function is serialized and sent to the front end. */ @@ -204,14 +237,6 @@ export type WidgetProps = { version?: string; }; -// TODO: more robust serialization of render function (with context?) -function to_esm({ - imports = "", - render, -}: Pick, "imports" | "render">) { - return `${imports}\nexport default { render: ${render.toString()} }`; -} - /** * Creates an anywidget for the Deno Jupyter kernel. * @@ -237,21 +262,21 @@ function to_esm({ * counter; // displays the widget * ``` * - * @param props - The properties of the widget. + * @param options - The properties of the widget. * @param props.state - The initial state of the widget (must be an object) * @param props.render - A function that renders the widget in the front end. This function is serialized and sent to the front end. * @param props.imports - The CDN ESM imports required for the front-end function. * @param props.version - The version of anywidget to use. */ -export function widget(props: WidgetProps): _Model { - let { state, render, imports, version } = props; +export function widget(options: WidgetOptions): Model { + let { state, render, imports, version } = options; let comm = new Comm({ anywidget_version: version }); let init_promise = comm .init() .then(() => comm.send_state({ ...state, _esm: to_esm({ imports, render }) }) ); - let model = new _Model(state); + let model = new Model(state); for (let key in state) { model.on(`change:${key}`, () => { comm.send_state({ [key]: model.get(key) }); From b667c9bfaa9af3b61b32257c9f34310ecc9a4625 Mon Sep 17 00:00:00 2001 From: Trevor Manz Date: Wed, 20 Mar 2024 12:54:58 -0400 Subject: [PATCH 4/4] update docs --- packages/deno/src/mod.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/deno/src/mod.ts b/packages/deno/src/mod.ts index 08de3f34..a289bf0d 100644 --- a/packages/deno/src/mod.ts +++ b/packages/deno/src/mod.ts @@ -224,7 +224,7 @@ function to_esm({ /** The options bag to pass to the {@link widget} method. */ export type WidgetOptions = { - /** The initial state of the widget. */ + /** The initial widget state. */ state: State; /** A function that renders the widget. This function is serialized and sent to the front end. */ render: (context: { @@ -233,7 +233,7 @@ export type WidgetOptions = { }) => unknown; /** The imports required for the front-end function. */ imports?: string; - /** The version of anywidget to use. */ + /** The version of the anywidget front end to use. */ version?: string; }; @@ -262,11 +262,7 @@ export type WidgetOptions = { * counter; // displays the widget * ``` * - * @param options - The properties of the widget. - * @param props.state - The initial state of the widget (must be an object) - * @param props.render - A function that renders the widget in the front end. This function is serialized and sent to the front end. - * @param props.imports - The CDN ESM imports required for the front-end function. - * @param props.version - The version of anywidget to use. + * @param options - The options for the widget {@link WidgetOptions}. */ export function widget(options: WidgetOptions): Model { let { state, render, imports, version } = options;