[loader] Add renderer ESM execution host and hybrid delegation #219

Closed
opened 2026-06-02 09:43:35 +00:00 by steve · 0 comments
Owner

Problem

First-party Lapis plugins need an ESM-first renderer execution path, while Obsidian-compatible and legacy plugins must continue to load through CommonJS compatibility.

Goal

Add renderer ESM execution and hybrid delegation so the loader can choose ESM, CommonJS, or sidecar execution from the shared runtime selector and diagnostics policy.

Scope

  • Add RendererEsmPluginExecutionHost using PluginAssetServer and dependency policy checks.
  • Add HybridCommunityPluginExecutionHost to delegate between ESM, CommonJS, and sidecar hosts.
  • Use selectPluginRuntimeEntry() for format and host decisions.
  • Resolve supported ESM module export shapes without instantiating in the execution host.
  • Fall back to CommonJS only when fallbackPath is explicitly declared or the plugin is Obsidian-compatible.
  • Add diagnostics for module format, fallback used, asset URL mode, and ESM import failures.

Non-goals

  • Do not hot-swap enabled ESM plugins without reload in v1.
  • Do not allow arbitrary plugin-defined import maps.
  • Do not remove CommonJS compatibility.

Acceptance Criteria

  • esm-basic style plugins load in web and Electron renderer.
  • Hybrid plugins fall back only when fallback is declared.
  • Obsidian-only plugins continue through CommonJS.
  • ESM import failures surface actionable diagnostics.

Implementation Notes

  • Keep instantiation in the existing PluginManager flow; the execution host should return a constructor or module shape.
  • Use versioned asset URLs so updates can produce distinct ESM module URLs.
  • Record fallback use for official registry plugins.

Active Implementation Plan

  • Add a renderer ESM execution host in packages/api/src/lib/plugin-manager.ts that uses the configured PluginAssetServer to obtain a versioned module URL, dynamically imports it with Vite ignored URL resolution, scans/prepares bare host dependencies for diagnostics, and returns the same constructor/module export shapes as the CommonJS host.
  • Add a hybrid community execution host that delegates CommonJS and sidecar-compatible requests to the existing host while routing structured renderer format: "esm" entries to the ESM host. ESM import failure may fall back to CommonJS only when the selected structured runtime entry declares fallbackPath.
  • Let PluginManager advertise renderer ESM support only when a PluginAssetServer is present, and select electron-renderer structured entries when the native bridge advertises plugin-assets; otherwise preserve existing workspace and sidecar behavior.
  • Pass the selected runtime entry into host evaluation so evaluation and diagnostics use the same selector result.
  • Add focused API tests for ESM loading, explicit fallback behavior, no-fallback failures, and CommonJS/Obsidian compatibility preservation.

Affected Packages and Files

  • packages/api/src/lib/plugin-manager.ts
  • packages/api/src/lib/__tests__/plugin-manager.test.ts
  • packages/api/spec.md
  • packages/workspace/spec.md
  • spec/src/30-cross-package-contracts/plugin-runtime.md
  • spec/src/20-packages/api/index.md
  • spec/src/20-packages/workspace/index.md

Suggested Files or Specs To Inspect

  • spec/src/30-cross-package-contracts/plugin-runtime.md
  • spec/src/20-packages/workspace/index.md
  • packages/api/src/lib/plugin-manager.ts
  • packages/api/src/lib/plugin-runtime-entry.ts
  • packages/workspace/src/lib/components/app/App.svelte

Validation Commands

  • pnpm --filter @lapis-notes/api exec vitest run src/lib/__tests__/plugin-manager.test.ts
  • pnpm --filter @lapis-notes/api check:types
  • pnpm --filter @lapis-notes/api check:all
  • make spec-lint
  • mdbook build spec
  • pnpm test:smoke

Follow-up Tasks

Implementation Summary

Fixed the renderer ESM plugin-manager test formatting regression so the API package format gate passes again.

Reopen Reason

Fix format regression in the renderer ESM execution host test from the last committed change.

## Problem First-party Lapis plugins need an ESM-first renderer execution path, while Obsidian-compatible and legacy plugins must continue to load through CommonJS compatibility. ## Goal Add renderer ESM execution and hybrid delegation so the loader can choose ESM, CommonJS, or sidecar execution from the shared runtime selector and diagnostics policy. ## Scope - Add `RendererEsmPluginExecutionHost` using `PluginAssetServer` and dependency policy checks. - Add `HybridCommunityPluginExecutionHost` to delegate between ESM, CommonJS, and sidecar hosts. - Use `selectPluginRuntimeEntry()` for format and host decisions. - Resolve supported ESM module export shapes without instantiating in the execution host. - Fall back to CommonJS only when `fallbackPath` is explicitly declared or the plugin is Obsidian-compatible. - Add diagnostics for module format, fallback used, asset URL mode, and ESM import failures. ## Non-goals - Do not hot-swap enabled ESM plugins without reload in v1. - Do not allow arbitrary plugin-defined import maps. - Do not remove CommonJS compatibility. ## Acceptance Criteria - `esm-basic` style plugins load in web and Electron renderer. - Hybrid plugins fall back only when fallback is declared. - Obsidian-only plugins continue through CommonJS. - ESM import failures surface actionable diagnostics. ## Implementation Notes - Keep instantiation in the existing `PluginManager` flow; the execution host should return a constructor or module shape. - Use versioned asset URLs so updates can produce distinct ESM module URLs. - Record fallback use for official registry plugins. ## Active Implementation Plan - Add a renderer ESM execution host in `packages/api/src/lib/plugin-manager.ts` that uses the configured `PluginAssetServer` to obtain a versioned module URL, dynamically imports it with Vite ignored URL resolution, scans/prepares bare host dependencies for diagnostics, and returns the same constructor/module export shapes as the CommonJS host. - Add a hybrid community execution host that delegates CommonJS and sidecar-compatible requests to the existing host while routing structured renderer `format: "esm"` entries to the ESM host. ESM import failure may fall back to CommonJS only when the selected structured runtime entry declares `fallbackPath`. - Let `PluginManager` advertise renderer ESM support only when a `PluginAssetServer` is present, and select `electron-renderer` structured entries when the native bridge advertises `plugin-assets`; otherwise preserve existing `workspace` and sidecar behavior. - Pass the selected runtime entry into host evaluation so evaluation and diagnostics use the same selector result. - Add focused API tests for ESM loading, explicit fallback behavior, no-fallback failures, and CommonJS/Obsidian compatibility preservation. ## Affected Packages and Files - `packages/api/src/lib/plugin-manager.ts` - `packages/api/src/lib/__tests__/plugin-manager.test.ts` - `packages/api/spec.md` - `packages/workspace/spec.md` - `spec/src/30-cross-package-contracts/plugin-runtime.md` - `spec/src/20-packages/api/index.md` - `spec/src/20-packages/workspace/index.md` ## Suggested Files or Specs To Inspect - `spec/src/30-cross-package-contracts/plugin-runtime.md` - `spec/src/20-packages/workspace/index.md` - `packages/api/src/lib/plugin-manager.ts` - `packages/api/src/lib/plugin-runtime-entry.ts` - `packages/workspace/src/lib/components/app/App.svelte` ## Validation Commands - `pnpm --filter @lapis-notes/api exec vitest run src/lib/__tests__/plugin-manager.test.ts` - `pnpm --filter @lapis-notes/api check:types` - `pnpm --filter @lapis-notes/api check:all` - `make spec-lint` - `mdbook build spec` - `pnpm test:smoke` ## Related Issues ## Follow-up Tasks ## Implementation Summary Fixed the renderer ESM plugin-manager test formatting regression so the API package format gate passes again. ## Reopen Reason Fix format regression in the renderer ESM execution host test from the last committed change. <!-- backlog:task_id=TASK-PLUGIN-LOADER-007 source_spec=spec/src/30-cross-package-contracts/plugin-runtime.md -->
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lapis-notes/lapis#219
No description provided.