From d5024a698263b881d3fc91f84bcb440a0def8036 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 11:27:10 +0000 Subject: [PATCH 1/4] Codegen: add --vscode-extension flag to inline extraImports wiring Closes #105. The Node-CJS codegen emitted a shim with an empty extraImports hook that every consumer hand-wired in a separate index.cjs to install the concrete vscode-API bindings. That boilerplate was 100% mechanical and identical across consumers. Add a --vscode-extension codegen flag that inlines the wiring so the generated .cjs is directly loadable as a VS Code extension's `main`: installs exports.extraImports calling the @hyperpolymath/affine-vscode adapter. Sub-flags --vscode-extension-adapter (override the require specifier) and --vscode-extension-no-lc (no language client; pass null) cover the documented variants. Migrate the editors/vscode pilot off the would-be hand-written index.cjs: its compile script now passes --vscode-extension and the regenerated out/extension.cjs carries the inline glue; package.json declares the adapter dependency. https://claude.ai/code/session_01FkzAgzpZFSGxzorVNZ9FUF --- bin/main.ml | 49 +++++++++++++++++-- editors/vscode/out/extension.cjs | 12 +++++ editors/vscode/package.json | 3 +- lib/codegen_node.ml | 75 ++++++++++++++++++++++++++++-- test/test_e2e.ml | 80 ++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+), 9 deletions(-) diff --git a/bin/main.ml b/bin/main.ml index 08209ac..75ff46a 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -477,7 +477,8 @@ let repl_cmd_fn () = (** Compile a file. With [--json], emits diagnostics for any compilation errors. With [--wasm-gc], targets the WebAssembly GC proposal instead of WASM 1.0 linear memory. *) -let compile_file face json wasm_gc path output = +let compile_file face json wasm_gc vscode_ext vscode_adapter vscode_no_lc + path output = let face = resolve_face ~quiet:json face path in if json then begin let diags = ref [] in @@ -659,7 +660,13 @@ let compile_file face json wasm_gc path output = (Affinescript.Codegen.show_codegen_error e); span = Affinescript.Span.dummy; help = None; labels = [] } | Ok wasm_module -> - let cjs = Affinescript.Codegen_node.emit_node_cjs wasm_module in + let cjs = + Affinescript.Codegen_node.emit_node_cjs + ~vscode_extension:vscode_ext + ?vscode_extension_adapter:vscode_adapter + ~vscode_extension_no_lc:vscode_no_lc + wasm_module + in let oc = open_out output in output_string oc cjs; close_out oc @@ -873,11 +880,18 @@ let compile_file face json wasm_gc path output = (Affinescript.Codegen.show_codegen_error e); `Error (false, "Node-CJS codegen error") | Ok wasm_module -> - let cjs = Affinescript.Codegen_node.emit_node_cjs wasm_module in + let cjs = + Affinescript.Codegen_node.emit_node_cjs + ~vscode_extension:vscode_ext + ?vscode_extension_adapter:vscode_adapter + ~vscode_extension_no_lc:vscode_no_lc + wasm_module + in let oc = open_out output in output_string oc cjs; close_out oc; - Format.printf "Compiled %s -> %s (Node-CJS)@." path output; + Format.printf "Compiled %s -> %s (Node-CJS%s)@." path output + (if vscode_ext then ", --vscode-extension" else ""); `Ok ()) else let optimized_prog = Affinescript.Opt.fold_constants_program prog in @@ -1141,6 +1155,29 @@ let wasm_gc_arg = Requires a runtime that supports the GC proposal: V8/Chrome ≥ 119, \ SpiderMonkey/Firefox ≥ 120, or Wasmtime with --wasm-features gc.") +(* Issue #105: --vscode-extension and its sub-flags. Only meaningful when + the output is a [.cjs] Node-CJS shim; ignored for other targets. *) +let vscode_ext_arg = + Arg.(value & flag & info ["vscode-extension"] + ~doc:"When emitting a Node-CJS shim (.cjs output), inline the vscode-API \ + wiring so the generated file is directly loadable as a VS Code \ + extension's `main` — no hand-written index.cjs or vendored adapter. \ + Installs exports.extraImports calling the \ + @hyperpolymath/affine-vscode adapter.") + +let vscode_adapter_arg = + Arg.(value & opt (some string) None & info ["vscode-extension-adapter"] + ~docv:"SPECIFIER" + ~doc:"Override the require() specifier for the vscode adapter used by \ + --vscode-extension (default: @hyperpolymath/affine-vscode). Useful \ + for testing against a local checkout or vendoring a custom adapter.") + +let vscode_no_lc_arg = + Arg.(value & flag & info ["vscode-extension-no-lc"] + ~doc:"With --vscode-extension, omit the vscode-languageclient/node \ + dependency for extensions that ship no language client; the \ + wiring passes null in its place.") + (** Shared --face flag: select the parser surface-syntax face. *) let face_arg = let faces = Arg.enum [ @@ -1486,7 +1523,9 @@ let repl_cmd = let compile_cmd = let doc = "Compile a file to WebAssembly (1.0 or GC proposal), Julia (.jl), JavaScript (.js), C (.c), a WGSL compute kernel (.wgsl), a Faust DSP program (.dsp), or an ONNX model (.onnx)" in let info = Cmd.info "compile" ~doc in - Cmd.v info Term.(ret (const compile_file $ face_arg $ json_arg $ wasm_gc_arg $ path_arg $ output_arg)) + Cmd.v info Term.(ret (const compile_file $ face_arg $ json_arg $ wasm_gc_arg + $ vscode_ext_arg $ vscode_adapter_arg $ vscode_no_lc_arg + $ path_arg $ output_arg)) let fmt_cmd = let doc = "Format a file" in diff --git a/editors/vscode/out/extension.cjs b/editors/vscode/out/extension.cjs index f4b4787..ee3000e 100644 --- a/editors/vscode/out/extension.cjs +++ b/editors/vscode/out/extension.cjs @@ -94,3 +94,15 @@ exports.deactivate = async function deactivate() { exports._registerHandle = _registerHandle; exports._getHandle = _getHandle; exports._freeHandle = _freeHandle; + +// Inserted by --vscode-extension (issue #105): auto-generated glue so this +// file is directly loadable as a VS Code extension's `main`. Replaces the +// previously hand-written index.cjs + vendored adapter boilerplate. +const _makeVscodeBindings = require("@hyperpolymath/affine-vscode"); +exports.extraImports = function() { + return _makeVscodeBindings( + require("vscode"), + require("vscode-languageclient/node"), + exports, + ); +}; diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 0eeccfe..a35d5e8 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -135,7 +135,7 @@ }, "scripts": { "vscode:prepublish": "npm run compile", - "compile": "affinescript compile src/extension.affine -o out/extension.cjs", + "compile": "affinescript compile src/extension.affine -o out/extension.cjs --vscode-extension", "watch": "echo 'watch mode not implemented for AffineScript source — re-run npm run compile'", "guard": "../../tools/check-no-extension-ts.sh", "package": "vsce package", @@ -146,6 +146,7 @@ "vsce": "^2.15.0" }, "dependencies": { + "@hyperpolymath/affine-vscode": "^0.1.0", "vscode-languageclient": "^9.0.0" } } diff --git a/lib/codegen_node.ml b/lib/codegen_node.ml index 317fb71..56abaae 100644 --- a/lib/codegen_node.ml +++ b/lib/codegen_node.ml @@ -96,9 +96,68 @@ let encode_module_to_bytes (m : wasm_module) : bytes = ) m.custom_sections; Bytes.of_string (Buffer.contents buf) +(** Escape a string for embedding inside a JS double-quoted literal. Only + the characters that would break out of (or corrupt) the literal are + escaped — sufficient for require() specifiers and module paths. *) +let js_string_escape (s : string) : string = + let buf = Buffer.create (String.length s + 8) in + String.iter (fun c -> + match c with + | '\\' -> Buffer.add_string buf "\\\\" + | '"' -> Buffer.add_string buf "\\\"" + | '\n' -> Buffer.add_string buf "\\n" + | '\r' -> Buffer.add_string buf "\\r" + | c -> Buffer.add_char buf c + ) s; + Buffer.contents buf + +(** Default npm specifier for the vscode-API adapter (issue #105). Callers + can override it via [~vscode_extension_adapter]. *) +let default_vscode_adapter = "@hyperpolymath/affine-vscode" + +(** Build the [--vscode-extension] wiring block (issue #105). + + Returns the JS that installs [exports.extraImports] so the generated + [.cjs] is directly loadable as a VS Code extension's [main] — no + hand-written [index.cjs], no vendored adapter. [adapter] is the + require() specifier for the adapter; when [no_lc] is set the extension + ships no language client, so the [vscode-languageclient/node] require + is skipped and [null] is passed in its place. *) +let vscode_extension_wiring ~(adapter : string) ~(no_lc : bool) : string = + let lc_arg = + if no_lc then "null" + else {|require("vscode-languageclient/node")|} + in + Printf.sprintf {| +// Inserted by --vscode-extension (issue #105): auto-generated glue so this +// file is directly loadable as a VS Code extension's `main`. Replaces the +// previously hand-written index.cjs + vendored adapter boilerplate. +const _makeVscodeBindings = require("%s"); +exports.extraImports = function() { + return _makeVscodeBindings( + require("vscode"), + %s, + exports, + ); +}; +|} (js_string_escape adapter) lc_arg + (** Wrap [m] in a Node-CJS shim. The shim is a single self-contained - JavaScript string suitable for writing to a [.cjs] file. *) -let emit_node_cjs ?(extra_imports_js : string option) (m : wasm_module) : string = + JavaScript string suitable for writing to a [.cjs] file. + + When [~vscode_extension:true] (issue #105), the shim additionally + installs [exports.extraImports] inline so the output is directly + loadable as a VS Code extension's [main]. [~vscode_extension_adapter] + overrides the adapter require() specifier (default + {!default_vscode_adapter}); [~vscode_extension_no_lc] omits the + [vscode-languageclient/node] dependency for extensions that ship no + language client. *) +let emit_node_cjs + ?(extra_imports_js : string option) + ?(vscode_extension : bool = false) + ?(vscode_extension_adapter : string option) + ?(vscode_extension_no_lc : bool = false) + (m : wasm_module) : string = let wasm_bytes = encode_module_to_bytes m in let b64 = base64_encode wasm_bytes in let extra = @@ -106,6 +165,16 @@ let emit_node_cjs ?(extra_imports_js : string option) (m : wasm_module) : string | Some js -> js | None -> "{}" in + let vscode_block = + if vscode_extension then + let adapter = + match vscode_extension_adapter with + | Some a -> a + | None -> default_vscode_adapter + in + vscode_extension_wiring ~adapter ~no_lc:vscode_extension_no_lc + else "" + in Printf.sprintf {|// Generated by AffineScript. Do not edit. // Node-CJS shim wrapping the compiled .wasm module so VS Code's extension // host (or any CJS consumer) can require() it directly. @@ -202,4 +271,4 @@ exports.deactivate = async function deactivate() { exports._registerHandle = _registerHandle; exports._getHandle = _getHandle; exports._freeHandle = _freeHandle; -|} b64 extra +%s|} b64 extra vscode_block diff --git a/test/test_e2e.ml b/test/test_e2e.ml index abb450d..fa5f417 100644 --- a/test/test_e2e.ml +++ b/test/test_e2e.ml @@ -2822,9 +2822,89 @@ let test_node_cjs_base64_roundtrip () = Alcotest.(check string) "matches RFC 4648 §10 vector" "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu" b64 +(* ---- Issue #105: --vscode-extension inline wiring ---- + + Verifies that emit_node_cjs ~vscode_extension:true inlines the + exports.extraImports glue (calling the @hyperpolymath/affine-vscode + adapter) so the generated .cjs is directly loadable as a VS Code + extension's `main` — no hand-written index.cjs / vendored adapter. *) + +let contains s sub = + let n = String.length sub and m = String.length s in + let rec scan i = i + n <= m && (String.sub s i n = sub || scan (i + 1)) in + n = 0 || scan 0 + +let cjs_of ?vscode_extension ?vscode_extension_adapter + ?vscode_extension_no_lc src = + let prog = parse_string_for_gc src in + match Codegen.generate_module prog with + | Error e -> Alcotest.failf "wasm codegen failed: %s" (Codegen.show_codegen_error e) + | Ok wasm_module -> + Codegen_node.emit_node_cjs ?vscode_extension ?vscode_extension_adapter + ?vscode_extension_no_lc wasm_module + +let activate_src = + {|pub fn activate(ctx_handle: Int) -> Int { return 0; } + pub fn deactivate() -> Int { return 0; }|} + +let test_vscode_extension_off_by_default () = + let cjs = cjs_of activate_src in + Alcotest.(check bool) "no extraImports assignment without the flag" + false (contains cjs "exports.extraImports = function"); + Alcotest.(check bool) "no adapter require without the flag" + false (contains cjs "@hyperpolymath/affine-vscode") + +let test_vscode_extension_inlines_wiring () = + let cjs = cjs_of ~vscode_extension:true activate_src in + Alcotest.(check bool) "installs exports.extraImports" + true (contains cjs "exports.extraImports = function"); + Alcotest.(check bool) "requires the default adapter" + true (contains cjs {|require("@hyperpolymath/affine-vscode")|}); + Alcotest.(check bool) "requires the vscode module" + true (contains cjs {|require("vscode")|}); + Alcotest.(check bool) "requires the language client" + true (contains cjs {|require("vscode-languageclient/node")|}); + Alcotest.(check bool) "passes exports as the host shim" + true (contains cjs "exports,\n );"); + (* The base shim (use strict, activate, handle table) is still present. *) + Alcotest.(check bool) "base shim still intact" + true (contains cjs "exports._registerHandle") + +let test_vscode_extension_adapter_override () = + let cjs = cjs_of ~vscode_extension:true + ~vscode_extension_adapter:"../local/adapter.cjs" activate_src in + Alcotest.(check bool) "uses the overridden adapter specifier" + true (contains cjs {|require("../local/adapter.cjs")|}); + Alcotest.(check bool) "does not fall back to the default adapter" + false (contains cjs "@hyperpolymath/affine-vscode") + +let test_vscode_extension_no_lc () = + let cjs = cjs_of ~vscode_extension:true ~vscode_extension_no_lc:true + activate_src in + Alcotest.(check bool) "skips the language-client require" + false (contains cjs "vscode-languageclient/node"); + Alcotest.(check bool) "passes null in its place" + true (contains cjs " null,\n"); + Alcotest.(check bool) "still requires vscode + adapter" + true (contains cjs {|require("vscode")|} + && contains cjs {|require("@hyperpolymath/affine-vscode")|}) + +let test_vscode_extension_adapter_escaped () = + (* A specifier containing a double quote must not break out of the JS + string literal. *) + let cjs = cjs_of ~vscode_extension:true + ~vscode_extension_adapter:{|a"b\c|} activate_src in + Alcotest.(check bool) "double quote and backslash are escaped" + true (contains cjs {|require("a\"b\\c")|}) + let codegen_node_tests = [ Alcotest.test_case "Node-CJS shim has all anchors (use strict, exports.activate, ...)" `Quick test_node_cjs_shim_shape; Alcotest.test_case "base64 encoder matches RFC 4648 vector" `Quick test_node_cjs_base64_roundtrip; + Alcotest.test_case "--vscode-extension off by default" `Quick test_vscode_extension_off_by_default; + Alcotest.test_case "--vscode-extension inlines extraImports wiring" `Quick test_vscode_extension_inlines_wiring; + Alcotest.test_case "--vscode-extension-adapter overrides the require specifier" `Quick test_vscode_extension_adapter_override; + Alcotest.test_case "--vscode-extension-no-lc skips the language client" `Quick test_vscode_extension_no_lc; + Alcotest.test_case "--vscode-extension-adapter specifier is JS-escaped" `Quick test_vscode_extension_adapter_escaped; ] (* ---- Stdlib parse + Core import regression ---- From 940c367a15ad3713c5062f5ec8031bd45526653d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 11:50:05 +0000 Subject: [PATCH 2/4] docs(affine-vscode): align adapter docs with --vscode-extension (issue #105) The README and mod.js still described the pre-#105 manual hookup and referred to the hook as `_extraImports`. The codegen shim uses `extraImports` and #105 now emits the wiring inline. - Document `--vscode-extension` (and the adapter / no-lc sub-flags) as the recommended path; keep a corrected manual fallback. - Fix the hook name `_extraImports` -> `extraImports`. - Fix the manual example's third argument: the adapter expects the .cjs shim module (it reads `_registerHandle` / `_instance` off it), not a `() => instance` thunk or a bare WebAssembly.Instance. https://claude.ai/code/session_01FkzAgzpZFSGxzorVNZ9FUF --- packages/affine-vscode/README.adoc | 41 ++++++++++++++++++++---------- packages/affine-vscode/mod.js | 17 ++++++++----- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/affine-vscode/README.adoc b/packages/affine-vscode/README.adoc index d8eb666..4c4bdbd 100644 --- a/packages/affine-vscode/README.adoc +++ b/packages/affine-vscode/README.adoc @@ -8,9 +8,29 @@ deliverable. == Usage -In your AffineScript-authored VS Code extension's CJS entry point, after -loading the `.cjs` produced by `affinescript compile extension.affine --o extension.cjs`, plug this adapter into the Wasm imports: +=== Recommended: `--vscode-extension` (issue #105) + +Compile with the `--vscode-extension` flag and the generated `.cjs` is +directly loadable as the extension's `main` — the adapter wiring is +emitted inline, so there is no hand-written entry point to maintain: + +[source,sh] +---- +affinescript compile src/extension.affine -o out/extension.cjs --vscode-extension +---- + +Point your extension's `package.json` `main` at the unmodified +`out/extension.cjs` and list `@hyperpolymath/affine-vscode` in +`dependencies`. Sub-flags: `--vscode-extension-adapter ` +overrides the adapter `require()` target; `--vscode-extension-no-lc` +omits the `vscode-languageclient/node` dependency. + +=== Manual wiring (fallback) + +If you cannot use the flag, set the shim's `extraImports` hook before the +first `activate`/`deactivate` call so the extern fns resolve to live +vscode APIs. Pass the shim module itself as the third argument — the +adapter reads its handle table and `_instance` lazily: [source,javascript] ---- @@ -18,18 +38,11 @@ const vscode = require("vscode"); const lc = require("vscode-languageclient/node"); const makeBindings = require("@hyperpolymath/affine-vscode"); -// The Node-CJS shim emitted by affinescript's compile -o *.cjs path lazily -// instantiates the wasm. Patch its `_extraImports()` hook before the first -// activate/deactivate call so the extern fns resolve to live vscode APIs. - -let instance; -const adapter = require("./extension.cjs"); -const original = adapter._extraImports || (() => ({})); -adapter._extraImports = () => makeBindings(vscode, lc, () => instance); +const shim = require("./extension.cjs"); +shim.extraImports = () => makeBindings(vscode, lc, shim); -// (Real wiring belongs in the codegen — Phase 3 will replace this manual -// hookup with auto-generated glue once `extension.affine` is the source of -// truth for the extension entry point.) +exports.activate = shim.activate; +exports.deactivate = shim.deactivate; ---- == Surface diff --git a/packages/affine-vscode/mod.js b/packages/affine-vscode/mod.js index e7dca03..c697cca 100644 --- a/packages/affine-vscode/mod.js +++ b/packages/affine-vscode/mod.js @@ -3,18 +3,21 @@ // // affine-vscode: JS-side adapter for stdlib/Vscode.affine + stdlib/VscodeLanguageClient.affine. // -// Issue #35 Phase 2 deliverable. Plug this into your Node-CJS extension's -// `_extraImports()` so each `extern fn` declared in the bindings resolves -// to the right vscode API call. +// Issue #35 Phase 2 deliverable. Resolves each `extern fn` declared in the +// bindings to the right vscode API call. // -// Usage from a hand-written .cjs (until Phase 3 automates the wiring): +// Preferred wiring (issue #105): compile with `--vscode-extension` and the +// generated .cjs installs `exports.extraImports` calling this adapter +// automatically — no hand-written entry point. // -// const vscodeBindings = require("@hyperpolymath/affine-vscode")( +// Manual wiring (fallback), from a hand-written .cjs: +// +// const shim = require("./extension.cjs"); +// shim.extraImports = () => require("@hyperpolymath/affine-vscode")( // require("vscode"), // require("vscode-languageclient/node"), -// instance, // WebAssembly.Instance, set after instantiate +// shim, // the .cjs shim module (hostShim) // ); -// // Pass vscodeBindings into the Wasm imports map under "env". // // The adapter maintains a per-process JS-side handle table keyed by Int // so opaque handles passed across the FFI boundary survive round-trips. From 766755f09eef10bb8279a9d7e3127f46020709da Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 11:51:24 +0000 Subject: [PATCH 3/4] chore: gitignore /_opam/ local opam switch A project-local opam switch (created for a working OCaml toolchain) is a machine-specific environment dir and must not be tracked, mirroring the existing /_build/ ignore. https://claude.ai/code/session_01FkzAgzpZFSGxzorVNZ9FUF --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 46a637e..fdc40b6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ Thumbs.db # Build /target/ /_build/ +/_opam/ /build/ /dist/ /out/ From f15371cbc2f3a42dfeb466fad6527d4325c41fb8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 11:55:44 +0000 Subject: [PATCH 4/4] feat(affine-vscode): npm package manifest + tag-driven publish (issue #104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In-repo half of #104 so consumers can `npm install @hyperpolymath/affine-vscode` instead of vendoring mod.js — which is what makes --vscode-extension's default `require("@hyperpolymath/affine-vscode")` resolvable downstream. - packages/affine-vscode/package.json: publish manifest (name, main, PMPL license matching the sibling deno.json, repository.directory, peerDependencies vscode + optional vscode-languageclient, files allowlist, public publishConfig). vscode-languageclient is marked optional to match --vscode-extension-no-lc. - .github/workflows/affine-vscode-publish.yml: publishes on a scoped `affine-vscode-v*` tag (kept distinct from the compiler's `v*` tags), verifies tag==package version, uses runner-provided Node and a runtime $HOME/.npmrc (no committed .npmrc, so the npm/bun blocker stays green). - README: npm-install section as the primary entry point. Out of scope (needs npm org/token + other repos): the @hyperpolymath npm org, first manual publish, and the my-lang / standards consumer ports. https://claude.ai/code/session_01FkzAgzpZFSGxzorVNZ9FUF --- .github/workflows/affine-vscode-publish.yml | 63 +++++++++++++++++++++ packages/affine-vscode/README.adoc | 11 ++++ packages/affine-vscode/package.json | 38 +++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 .github/workflows/affine-vscode-publish.yml create mode 100644 packages/affine-vscode/package.json diff --git a/.github/workflows/affine-vscode-publish.yml b/.github/workflows/affine-vscode-publish.yml new file mode 100644 index 0000000..35e807d --- /dev/null +++ b/.github/workflows/affine-vscode-publish.yml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: PMPL-1.0-or-later +# Publishes @hyperpolymath/affine-vscode to npm on a scoped tag push. +# +# This repo is Deno-first (see CLAUDE.md). The npm publish here is a +# deliberate, owner-sanctioned exception (issue #104): the VS Code +# extension host is npm-native and cannot consume the Deno/JSR manifest, +# so AffineScript-authored extensions resolve the adapter via +# `require("@hyperpolymath/affine-vscode")`. Only the publish step touches +# npm; no npm runtime deps are added to the repo and no .npmrc is +# committed (the auth file is written to $HOME at runtime). +# +# Trigger: push a tag matching `affine-vscode-v*` (e.g. affine-vscode-v0.1.0). +# This pattern is intentionally distinct from the `v*` tags used by the +# OCaml compiler Release workflow so the two never collide. +# +# Requires repository secret NPM_TOKEN (an npm automation token with +# publish rights to the @hyperpolymath scope). + +name: Publish affine-vscode + +on: + push: + tags: + - 'affine-vscode-v*' + +permissions: + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Verify tag matches package version + working-directory: packages/affine-vscode + run: | + tag="${GITHUB_REF_NAME#affine-vscode-v}" + pkg="$(node -p "require('./package.json').version")" + if [ "$tag" != "$pkg" ]; then + echo "❌ Tag version ($tag) does not match package.json version ($pkg)" >&2 + exit 1 + fi + echo "✅ Publishing @hyperpolymath/affine-vscode@$pkg" + + - name: Configure npm auth + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + if [ -z "${NPM_TOKEN}" ]; then + echo "❌ NPM_TOKEN secret is not set" >&2 + exit 1 + fi + echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "${HOME}/.npmrc" + + - name: Publish to npm + working-directory: packages/affine-vscode + run: npm publish --access public + + - name: Clean up npm auth + if: always() + run: rm -f "${HOME}/.npmrc" diff --git a/packages/affine-vscode/README.adoc b/packages/affine-vscode/README.adoc index 4c4bdbd..0d2b200 100644 --- a/packages/affine-vscode/README.adoc +++ b/packages/affine-vscode/README.adoc @@ -6,6 +6,17 @@ JS-side adapter for the `stdlib/Vscode.affine` and `stdlib/VscodeLanguageClient.affine` binding modules. Issue #35 Phase 2 deliverable. +== Install + +[source,sh] +---- +npm install @hyperpolymath/affine-vscode +---- + +Add it to your extension's `package.json` `dependencies`. `vscode` and +`vscode-languageclient` are peer dependencies (the latter is optional — +omit it with `--vscode-extension-no-lc`). + == Usage === Recommended: `--vscode-extension` (issue #105) diff --git a/packages/affine-vscode/package.json b/packages/affine-vscode/package.json new file mode 100644 index 0000000..9b41d9f --- /dev/null +++ b/packages/affine-vscode/package.json @@ -0,0 +1,38 @@ +{ + "name": "@hyperpolymath/affine-vscode", + "version": "0.1.0", + "description": "JS-side adapter for AffineScript's stdlib/Vscode.affine + stdlib/VscodeLanguageClient.affine binding modules. Resolves each extern fn to a real vscode / vscode-languageclient call and maintains the FFI handle table.", + "main": "./mod.js", + "license": "PMPL-1.0-or-later", + "repository": { + "type": "git", + "url": "https://github.com/hyperpolymath/affinescript.git", + "directory": "packages/affine-vscode" + }, + "homepage": "https://github.com/hyperpolymath/affinescript/tree/main/packages/affine-vscode", + "bugs": "https://github.com/hyperpolymath/affinescript/issues", + "keywords": [ + "affinescript", + "vscode", + "vscode-extension", + "language-server", + "wasm", + "ffi-adapter" + ], + "files": [ + "mod.js", + "README.adoc" + ], + "peerDependencies": { + "vscode": "*", + "vscode-languageclient": "^9.0.0" + }, + "peerDependenciesMeta": { + "vscode-languageclient": { + "optional": true + } + }, + "publishConfig": { + "access": "public" + } +}