[lexical][lexical-extension][*] Feature: Lexical Extension#7706
[lexical][lexical-extension][*] Feature: Lexical Extension#7706etrepum merged 98 commits intofacebook:mainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…he esbuild bug is worked around
zurfyx
left a comment
There was a problem hiding this comment.
This is a brilliant piece of work, particularly the API design, there's been many ideas around extensions/bundling plugins but this one is certainly the most complete that appears to ticks the most boxes. Thank you for writing this comprehensive multi-page documentation!
Admittedly, there's a lot going on here and didn't read line by line, but I wanted to share some high-level feedback to understand better.
configExtension(EmojiExtension, {
emojiClass: "emoji-node",
emojiLoadedClass: "emoji-node-loaded",
emojiBaseUrl: "/assets/emoji",
}),
When is the configExtension required? How do users know? TS works well either way.
From the docs: No compile-time support for dependency resolution
Can we actually do this with TypeScript? Or would it require some build script?
I don't know if it's releated, but I still foresee the possibility of conflicting dependencies, especially in the case of 2 third parties that haven't seen in other. I recall we discussed this in the past, and I'm not sure if it's worrysome or a problem worth investing in further.
How much did you get to test Flow? Is it a best effort? Since Meta is probably one of the few users, I don't think this is a blocker and we can test, and report/fix accordingly.
I'm stamping already since this is an idea that has been floating for some time and discussed in some sessions but I would value if others had a chance to read over it (at least the docs) and provide some feedback.
| // inlineImage: 'inline-editor-image', | ||
| // layoutContainer: 'PlaygroundEditorTheme__layoutContainer', | ||
| // layoutItem: 'PlaygroundEditorTheme__layoutItem', |
There was a problem hiding this comment.
All of the commented, are not implemented but are listed for reference?
There was a problem hiding this comment.
Yes, those are classes that I did not implement in the tailwind theme. Most of them are playground only and don't really make sense in something designed to be used elsewhere. The goal of the tailwind extension is to have a starter theme that works straight out of the box for many people.
| declare export var AutoFocusExtension: LexicalExtension<AutoFocusConfig, "@lexical/extension/AutoFocus", NamedSignalsOutput<AutoFocusConfig>, void>; | ||
| export type ClearEditorConfig = { | ||
| $onClear: () => void; | ||
| } | ||
| declare export var ClearEditorExtension: LexicalExtension<ClearEditorConfig, "@lexical/extension/ClearEditor", NamedSignalsOutput<ClearEditorConfig>, void>; |
There was a problem hiding this comment.
Is it intended that these load from the same module? Is the thinking process that they're small and common enough to be bundled?
There was a problem hiding this comment.
Small, common enough to be bundled, and tree shaking should eliminate it if you don't use it (at least with the standard toolchains like vite/rollup/esbuild). We could move definitions of common extensions to a separate module from the extension runtime but I think that's just busywork if you assume that tree shaking works.
| }; | ||
| const app = useMemo( | ||
| () => | ||
| defineExtension({ |
There was a problem hiding this comment.
Given the popularity of the playground to copy-paste code, should we consider expanding this to have more dependencies plugins?
There was a problem hiding this comment.
Absolutely, I wanted to do that separately to keep the scope down and also to show that you can migrate to lexical extension incrementally
Thank you! There's more to write, but I really want to get something out there sooner than later and questions will drive where effort is spend on docs.
It's possible but not really worth it because there are already several ways to get a very early runtime error while the editor is being constructed when there are conflicting names or a name matches something in a conflictWith from another extension. It would really just make type checking slower, more fragile, harder to maintain, etc. for very little benefit.
The recommendation of using scoped names should mostly eliminate the third party problem, two parties shouldn't publish extensions with the same scoped name. Realistically though there are much more insidious ways for extensions to conflict that we really can't do anything about (e.g. handling the same DOM events or commands in incompatible ways, the simplest of which would be that one handles it completely and prevents the other extension from seeing the event or command it needs to see).
Would be great to get some more feedback! I don't think we'll hear much from non-maintainers until after it's released, which would be nice to do sooner than later. It would also let me clean up some of the hacks that the examples are using to reference packages that don't exist on npm. |
Breaking Changes
DecoratorNodeDecoratorNodeto implementdecorate()decorate(): Tto bedecorate(): null | Tas that's always how it worked in practice - the generic type here is unsafe and wrong anyway (e.g.$isDecoratorNodeis a cast to any typeT)Description
Lexical plug-ins don't do what people want them to do. They are just an awkward convention for React components that involve a lot of boilerplate around
useEffect. It encourages people to write unnecessarily React-dependent code and still manually manage any configuration. The kind of extension mechanism that Lexical needs combines both editor configuration and/or runtime behavior in a framework agnostic manner, but still allow for framework-specific behavior when useful.This PR is based on the Lexical Builder prototype.
Requirements
Status
Lexical Extension Docs
This PR is in a draft state that currently implements:
lexical(src/extension-core): the types and utility functions required to define extensions@lexical/extension: the mechanism to build an editor from extensions, and a few extensions that are essentially only available in@lexical/reactas features of LexicalComposer or separate plug-ins (AutoFocusExtension, TabExtension, etc.) - uses@preact/signals-coreto make it feasible to do what's done with the react hooks outside of that lifecycle@lexical/link- LinkExtension and AutoLinkExtension@lexical/hashtag- HashtagExtension@lexical/history- HistoryExtension and SharedHistoryExtension@lexical/dragon- DragonExtension@lexical/plain-text- PlainTextExtension@lexical/rich-text- RichTextExtension@lexical/react- ReactProviderExtension, ReactPluginHost, etc.@lexical/table- TableExtension@lexical/playground- A minimal migration from LexicalComposer to LexicalExtensionComposerExamples:
@lexical/reactNot in Scope
This PR does not implement extensions for collab, markdown, Prism code highlighting, and some other functionality available as React plug-ins.
Test plan
LexicalExtensionComposerforLexicalComposer