Skip to content

feat(core): expose framework-adapter subpaths via exports map#273

Open
sted wants to merge 2 commits intoeigenpal:mainfrom
sted:chore/subpath-exports
Open

feat(core): expose framework-adapter subpaths via exports map#273
sted wants to merge 2 commits intoeigenpal:mainfrom
sted:chore/subpath-exports

Conversation

@sted
Copy link
Copy Markdown
Contributor

@sted sted commented Apr 17, 2026

Summary

  • feat(core): expose framework-adapter subpaths via exports map
    Adds explicit subpath exports for internals that framework adapters
    (outside packages/{react,vue}) need to consume without reaching into
    src/. Replaces the ./* catch-all with a curated surface at directory
    boundaries.
    New subpaths:
    ./prosemirror, ./prosemirror/extensions, ./prosemirror/editor.css
    ./layout-engine, ./layout-painter
    ./layout-bridge/{toFlowBlocks,measuring,clickToPositionDom,selectionRects}
    ./managers, ./plugin-api, ./types, ./utils/textSelection
    Also:
  • tsup.config.ts: add matching entry keys so dist mirrors src layout
  • scripts/copy-assets.mjs: copy editor.css into dist/ post-build
  • layout-painter: re-export HeaderFooterContent type
  • Move prosemirror-* from dependencies to peerDependencies: consumers
    own the PM version, which avoids duplicate PM copies in consumer
    bundles (previously needed resolve.dedupe workarounds).

Test plan

  • bun run typecheck passes
  • bun test passes
  • bun run build succeeds

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 17, 2026

@sted is attempting to deploy a commit to the EigenPal Team on Vercel.

A member of the Team first needs to authorize it.

@sted
Copy link
Copy Markdown
Contributor Author

sted commented Apr 17, 2026

Closing this for now — needs more work before it's ready.

While validating the new subpath surface from an external framework adapter (consuming the built dist via npm), we hit an evaluation-order bug: schema/index.ts does new ExtensionManager(createStarterKit()) at module top-level, and several extensions (e.g. BidiShortcutExtension) import singletonManager back from that module. The resulting circular ESM cycle works by accident under monorepo workspace aliasing (source-level resolution) but fails when the package is consumed as a built bundle — var bindings for extension factories aren't yet assigned when the singleton constructor runs, producing X is not a function.

The fix belongs with this PR, not as a follow-up — shipping the subpath exports without it would expose a surface that doesn't actually work for external consumers. Will reopen once we've validated a lazy-init (or cycle-break) fix end-to-end.

Keeping the branch around; thanks for your patience.

@sted sted closed this Apr 17, 2026
@sted sted reopened this Apr 17, 2026
@sted
Copy link
Copy Markdown
Contributor Author

sted commented Apr 17, 2026

Reopened with the init-order fix. Scope now:

  1. feat(core): expose framework-adapter subpaths via exports map — the original commit (subpath exports, tsup entries, PM libs → peerDependencies, CSS copy step)
  2. fix(core): plumb ExtensionManager through ExtensionContext — new: breaks the schema → StarterKit → extensions → schema circular import by passing the manager via ExtensionContext instead of having extensions re-import singletonManager. No public API change (singletonManager and schema top-level exports untouched). Only one extension (BidiShortcutExtension) actually needed the change.

Validated end-to-end: built dist consumed from an external Svelte adapter, editor loads, commands work.

sted added 2 commits April 17, 2026 15:38
Adds explicit subpath exports for internals that framework adapters
(outside packages/{react,vue}) need to consume without reaching into
src/. Replaces the `./*` catch-all with a curated surface at directory
boundaries.

New subpaths:
  ./prosemirror, ./prosemirror/extensions, ./prosemirror/editor.css
  ./layout-engine, ./layout-painter
  ./layout-bridge/{toFlowBlocks,measuring,clickToPositionDom,selectionRects}
  ./managers, ./plugin-api, ./types, ./utils/textSelection

Also:
- tsup.config.ts: add matching entry keys so dist mirrors src layout
- scripts/copy-assets.mjs: copy editor.css into dist/ post-build
- layout-painter: re-export HeaderFooterContent type
- Move prosemirror-* from dependencies to peerDependencies: consumers
  own the PM version, which avoids duplicate PM copies in consumer
  bundles (previously needed resolve.dedupe workarounds).
Break the `schema` → `StarterKit` → `extensions` → `schema` circular
import that causes `X is not a function` when the package is consumed
as a built bundle.

Root cause: `BidiShortcutExtension` imported `singletonManager` directly
from `../../schema`. The resulting cycle works by accident under
monorepo workspace aliasing (source-level ESM with live bindings) but
fails when bundled — `var` bindings for extension factories aren't yet
assigned when `schema/index.ts`'s top-level `new ExtensionManager(
createStarterKit())` executes.

Fix: give extensions access to their own manager via `ExtensionContext`
instead of reaching back to a global singleton.

- types.ts: add `manager: ExtensionManager` to ExtensionContext
- ExtensionManager.ts: pass `this` when building ctx for onSchemaReady
- BidiShortcutExtension.ts: use ctx.manager.getCommands() instead of
  the imported singletonManager.getCommands()

No API surface change for consumers. `singletonManager` and `schema`
top-level exports remain untouched. Tests updated to pass a stub
manager.

Arguably better design independently of the bundling issue: extensions
shouldn't reach back to a module-level singleton; their manager is
already in scope via the context they receive.
@sted sted force-pushed the chore/subpath-exports branch from 1b74508 to b044bb6 Compare April 17, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant