@lexical/html 0.44.1-nightly.20260519.0 → 0.45.1-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/{DOMRenderExtension.d.ts → dist/DOMRenderExtension.d.ts} +12 -1
  2. package/dist/DOMRenderRuntime.d.ts +51 -0
  3. package/dist/LexicalHtml.dev.js +3289 -0
  4. package/dist/LexicalHtml.dev.mjs +3242 -0
  5. package/{LexicalHtml.js.flow → dist/LexicalHtml.js.flow} +16 -16
  6. package/dist/LexicalHtml.mjs +57 -0
  7. package/dist/LexicalHtml.node.mjs +55 -0
  8. package/dist/LexicalHtml.prod.js +9 -0
  9. package/dist/LexicalHtml.prod.mjs +9 -0
  10. package/dist/RenderContext.d.ts +68 -0
  11. package/{compileDOMRenderConfigOverrides.d.ts → dist/compileDOMRenderConfigOverrides.d.ts} +1 -1
  12. package/{constants.d.ts → dist/constants.d.ts} +2 -0
  13. package/dist/domOverride.d.ts +23 -0
  14. package/dist/import/CoreImportExtension.d.ts +11 -0
  15. package/dist/import/DOMImportExtension.d.ts +82 -0
  16. package/dist/import/HorizontalRuleImportExtension.d.ts +28 -0
  17. package/dist/import/ImportContext.d.ts +208 -0
  18. package/dist/import/compileImportRules.d.ts +50 -0
  19. package/dist/import/coreImportRules.d.ts +25 -0
  20. package/dist/import/defineImportRule.d.ts +32 -0
  21. package/dist/import/defineOverlayRules.d.ts +66 -0
  22. package/dist/import/index.d.ts +38 -0
  23. package/dist/import/inlineStylesFromStyleSheets.d.ts +28 -0
  24. package/dist/import/parseCss.d.ts +18 -0
  25. package/dist/import/runImport.d.ts +19 -0
  26. package/dist/import/schemas.d.ts +106 -0
  27. package/dist/import/sel.d.ts +74 -0
  28. package/dist/import/types.d.ts +394 -0
  29. package/dist/index.d.ts +44 -0
  30. package/{types.d.ts → dist/types.d.ts} +96 -8
  31. package/package.json +33 -18
  32. package/src/ContextRecord.ts +243 -0
  33. package/src/DOMRenderExtension.ts +96 -0
  34. package/src/DOMRenderRuntime.ts +265 -0
  35. package/src/RenderContext.ts +168 -0
  36. package/src/compileDOMRenderConfigOverrides.ts +416 -0
  37. package/src/constants.ts +18 -0
  38. package/src/domOverride.ts +46 -0
  39. package/src/import/CoreImportExtension.ts +26 -0
  40. package/src/import/DOMImportExtension.ts +221 -0
  41. package/src/import/HorizontalRuleImportExtension.ts +52 -0
  42. package/src/import/ImportContext.ts +339 -0
  43. package/src/import/compileImportRules.ts +178 -0
  44. package/src/import/coreImportRules.ts +545 -0
  45. package/src/import/defineImportRule.ts +40 -0
  46. package/src/import/defineOverlayRules.ts +105 -0
  47. package/src/import/index.ts +97 -0
  48. package/src/import/inlineStylesFromStyleSheets.ts +104 -0
  49. package/src/import/parseCss.ts +219 -0
  50. package/src/import/runImport.ts +245 -0
  51. package/src/import/schemas.ts +280 -0
  52. package/src/import/sel.ts +314 -0
  53. package/src/import/types.ts +471 -0
  54. package/src/index.ts +561 -0
  55. package/src/types.ts +470 -0
  56. package/LexicalHtml.dev.js +0 -914
  57. package/LexicalHtml.dev.mjs +0 -900
  58. package/LexicalHtml.mjs +0 -24
  59. package/LexicalHtml.node.mjs +0 -22
  60. package/LexicalHtml.prod.js +0 -9
  61. package/LexicalHtml.prod.mjs +0 -9
  62. package/RenderContext.d.ts +0 -32
  63. package/domOverride.d.ts +0 -18
  64. package/index.d.ts +0 -32
  65. /package/{ContextRecord.d.ts → dist/ContextRecord.d.ts} +0 -0
  66. /package/{LexicalHtml.js → dist/LexicalHtml.js} +0 -0
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type {DOMImportContextSymbol} from '../constants';
9
+ import type {
10
+ AnyContextConfigPairOrUpdater,
11
+ ContextConfig,
12
+ ContextRecord,
13
+ } from '../types';
14
+ import type {CompiledOverlayRules} from './defineOverlayRules';
15
+ import type {LexicalNode} from 'lexical';
16
+
17
+ /**
18
+ * Phantom-typed branding so consumers cannot construct or mutate a
19
+ * {@link CompiledSelector} directly; the only way to obtain one is via the
20
+ * {@link sel} builder or {@link parseSelector}. The actual runtime shape is
21
+ * an internal implementation detail (see `./sel`).
22
+ *
23
+ * @experimental
24
+ */
25
+ export declare const NodeBrand: unique symbol;
26
+ /** @experimental */
27
+ export declare const CaptureBrand: unique symbol;
28
+
29
+ /**
30
+ * An opaque, compiled selector used as the `match` field of a
31
+ * {@link DOMImportRule}. The two phantom type parameters carry the matched
32
+ * Node subtype (`N`) and a record of named regex captures (`C`) so the
33
+ * importer body gets correctly-typed `ctx` and `node` arguments without
34
+ * casts.
35
+ *
36
+ * @experimental
37
+ */
38
+ export interface CompiledSelector<
39
+ N extends Node = Node,
40
+ C extends Record<string, RegExpMatchArray> = Record<string, RegExpMatchArray>,
41
+ > {
42
+ readonly [NodeBrand]?: N;
43
+ readonly [CaptureBrand]?: C;
44
+ }
45
+
46
+ /**
47
+ * The Node subtype matched by a selector (e.g. `HTMLAnchorElement` for
48
+ * `sel.tag('a')`, `Text` for `sel.text()`).
49
+ *
50
+ * @experimental
51
+ */
52
+ export type NodeOfSelector<S> =
53
+ S extends CompiledSelector<infer N, Record<string, RegExpMatchArray>>
54
+ ? N
55
+ : Node;
56
+
57
+ /**
58
+ * The named-capture map for a selector.
59
+ *
60
+ * @experimental
61
+ */
62
+ export type CapturesOfSelector<S> =
63
+ S extends CompiledSelector<Node, infer C> ? C : Record<string, never>;
64
+
65
+ /**
66
+ * Options bag for {@link ElementSelectorBuilder.attr} when the value is a
67
+ * regex. Future options will be added here without breaking existing
68
+ * call-sites.
69
+ *
70
+ * @experimental
71
+ */
72
+ export interface AttrMatchOptions<K extends string = string> {
73
+ /**
74
+ * If provided, the {@link RegExpMatchArray} from the successful match is
75
+ * stored on `ctx.captures[capture]` for the importer to consume — saving
76
+ * a second regex execution.
77
+ */
78
+ readonly capture?: K;
79
+ }
80
+
81
+ /**
82
+ * Options bag for {@link ElementSelectorBuilder.styleAny} when the value is a
83
+ * regex. See {@link AttrMatchOptions} for capture semantics.
84
+ *
85
+ * @experimental
86
+ */
87
+ export interface StyleMatchOptions<K extends string = string> {
88
+ readonly capture?: K;
89
+ }
90
+
91
+ /**
92
+ * Fluent builder for an element selector. The two type parameters carry the
93
+ * matched element type and the named-capture map; each call refines them.
94
+ *
95
+ * The builder itself implements {@link CompiledSelector} so it can be used
96
+ * directly as the `match` field of a rule — no `.build()` call needed.
97
+ *
98
+ * @experimental
99
+ */
100
+ export interface ElementSelectorBuilder<
101
+ E extends HTMLElement,
102
+ C extends Record<string, RegExpMatchArray> = Record<string, never>,
103
+ > extends CompiledSelector<E, C> {
104
+ /** Require every listed class to be present on the element. */
105
+ classAll(...classes: readonly string[]): ElementSelectorBuilder<E, C>;
106
+ /** Require at least one of the listed classes to be present. */
107
+ classAny(...classes: readonly string[]): ElementSelectorBuilder<E, C>;
108
+ /** Require the attribute to be present (any value). */
109
+ attr(name: string, value: true): ElementSelectorBuilder<E, C>;
110
+ /** Require the attribute to equal the given string. */
111
+ attr(name: string, value: string): ElementSelectorBuilder<E, C>;
112
+ /**
113
+ * Require the attribute to match the given regex. With
114
+ * `{capture: 'name'}` the match result is exposed on
115
+ * `ctx.captures.name`.
116
+ */
117
+ attr<const O extends AttrMatchOptions>(
118
+ name: string,
119
+ value: RegExp,
120
+ options?: O,
121
+ ): ElementSelectorBuilder<
122
+ E,
123
+ O extends {capture: infer K} ? C & Record<K & string, RegExpMatchArray> : C
124
+ >;
125
+ /** Require the inline-style declaration to equal `value`. */
126
+ styleAny(prop: string, value: string): ElementSelectorBuilder<E, C>;
127
+ /** Require the inline-style declaration to match `value`. */
128
+ styleAny<const O extends StyleMatchOptions>(
129
+ prop: string,
130
+ value: RegExp,
131
+ options?: O,
132
+ ): ElementSelectorBuilder<
133
+ E,
134
+ O extends {capture: infer K} ? C & Record<K & string, RegExpMatchArray> : C
135
+ >;
136
+ }
137
+
138
+ /**
139
+ * Argument to {@link DOMImportContext.branch} / `$importChildren({context})`
140
+ * — see {@link ContextConfigPair} / {@link ContextConfigUpdater}.
141
+ *
142
+ * @experimental
143
+ */
144
+ export type ImportContextPairOrUpdater = AnyContextConfigPairOrUpdater<
145
+ typeof DOMImportContextSymbol
146
+ >;
147
+
148
+ /**
149
+ * A typed context-state key for the import pipeline. Create with
150
+ * {@link createImportState}.
151
+ *
152
+ * @experimental
153
+ */
154
+ export type ImportStateConfig<V> = ContextConfig<
155
+ typeof DOMImportContextSymbol,
156
+ V
157
+ >;
158
+
159
+ /**
160
+ * A mutable, document-order-shared store for the import pipeline. Lets a
161
+ * rule visited early in the document write information that rules visited
162
+ * later can read — e.g. parse `<style>` or `<meta>` and influence
163
+ * subsequent matching.
164
+ *
165
+ * Implemented as the root-layer {@link ContextRecord} of the import walk:
166
+ * `ctx.session.set(cfg, v)` mutates the slot on that root record, and
167
+ * every unshadowed `ctx.get(cfg)` read in any branch picks it up. A
168
+ * `$importChildren({context: [...]})` branch that explicitly writes the
169
+ * same slot shadows the session value for the duration of that branch.
170
+ *
171
+ * @experimental
172
+ */
173
+ export interface ImportSession {
174
+ /** Read the current value, returning the config's default if unset. */
175
+ get<V>(cfg: ImportStateConfig<V>): V;
176
+ /** Write `value` into the slot. */
177
+ set<V>(cfg: ImportStateConfig<V>, value: V): void;
178
+ /** Read-modify-write. */
179
+ update<V>(cfg: ImportStateConfig<V>, updater: (prev: V) => V): void;
180
+ /** Returns `true` if the slot has been written since session creation. */
181
+ has<V>(cfg: ImportStateConfig<V>): boolean;
182
+ }
183
+
184
+ /**
185
+ * Context exposed to a rule's `$import` function. Mirrors the existing render
186
+ * context (see {@link RenderContext}) but is import-scoped.
187
+ *
188
+ * @experimental
189
+ */
190
+ export interface DOMImportContext<
191
+ C extends Record<string, RegExpMatchArray> = Record<string, never>,
192
+ > {
193
+ /** Captures from this rule's selector. Fresh per rule invocation. */
194
+ readonly captures: Readonly<C>;
195
+ /**
196
+ * Mutable, document-order-shared store. Use to make information from
197
+ * earlier-visited nodes available to later-visited ones (e.g. a
198
+ * `<style>` or `<meta>` at the top of the document influencing how
199
+ * later elements are interpreted). One {@link ImportSession} instance
200
+ * is created per top-level `$generateNodesFromDOM` call and is shared
201
+ * across all recursive `$importChildren` / `$importOne` invocations.
202
+ */
203
+ readonly session: ImportSession;
204
+
205
+ /** Read a typed context value. */
206
+ get<V>(cfg: ImportStateConfig<V>): V;
207
+
208
+ /**
209
+ * Recursively import every child of `parent` and return the produced
210
+ * lexical nodes, optionally enforcing a {@link ChildSchema} and/or
211
+ * branching the import context for the duration of the call (via
212
+ * `opts.context`).
213
+ */
214
+ $importChildren(parent: ParentNode, opts?: ImportChildrenOpts): LexicalNode[];
215
+
216
+ /**
217
+ * Recursively import a single DOM node.
218
+ */
219
+ $importOne(node: Node, opts?: ImportNodeOpts): LexicalNode[];
220
+ }
221
+
222
+ /**
223
+ * Options accepted by {@link DOMImportContext.$importChildren}. The combination
224
+ * of `schema`, `$onChild`, and `$after` is sufficient to express every
225
+ * child-handling pattern in the legacy `forChild` / `after` / wrap-continuous
226
+ * machinery.
227
+ *
228
+ * @experimental
229
+ */
230
+ export interface ImportChildrenOpts {
231
+ /**
232
+ * How to validate and (re)package produced children. Defaults to whichever
233
+ * schema the parent's importer passed; the top-level entry uses
234
+ * {@link BlockSchema}.
235
+ */
236
+ readonly schema?: ChildSchema;
237
+ /**
238
+ * Called for each produced lexical child immediately after its rule
239
+ * returned, with the chance to substitute or drop it. Equivalent to the
240
+ * old `forChild` hook but scoped to one `$importChildren` call.
241
+ */
242
+ readonly $onChild?: (child: LexicalNode) => LexicalNode | null | undefined;
243
+ /**
244
+ * Called once with the full child array after all DOM children have been
245
+ * recursively imported but before {@link ChildSchema.$packageRun} is
246
+ * applied. Equivalent to the old `after` hook.
247
+ */
248
+ readonly $after?: (children: LexicalNode[]) => LexicalNode[];
249
+ /** Context overrides scoped to the children traversal. */
250
+ readonly context?: readonly ImportContextPairOrUpdater[];
251
+ /**
252
+ * Additional {@link DOMImportRule}s active only for this children
253
+ * traversal (and any nested `$importChildren` calls that don't push
254
+ * their own overlay). The overlay is checked BEFORE the main
255
+ * dispatcher, so its rules take precedence; calling `$next()` from an
256
+ * overlay rule falls through to the next overlay-or-main rule.
257
+ *
258
+ * Use this to scope cost-bearing rules to where they apply. For
259
+ * example, a GitHub code-table rule installs an overlay that
260
+ * unwraps `<tr>` / `<td>` inside the table, without paying that
261
+ * predicate cost on every other `<tr>` / `<td>` paste.
262
+ *
263
+ * The value must be produced by
264
+ * {@link defineOverlayRules}; this forces the dispatcher to be
265
+ * compiled once at module scope and reused across
266
+ * `$importChildren` calls, instead of being recompiled per invocation.
267
+ */
268
+ readonly rules?: CompiledOverlayRules;
269
+ }
270
+
271
+ /** @experimental */
272
+ export interface ImportNodeOpts {
273
+ readonly context?: readonly ImportContextPairOrUpdater[];
274
+ }
275
+
276
+ /**
277
+ * A {@link ChildSchema} encodes which lexical nodes a parent accepts as
278
+ * children and how to package or reject the rest. The legacy
279
+ * `wrapContinuousInlines` / `ArtificialNode__DO_NOT_USE` logic is the
280
+ * `BlockSchema` and `NestedBlockSchema` cases of this primitive.
281
+ *
282
+ * A schema only controls how the *children* are assembled before being
283
+ * appended to the parent the calling rule already chose. It cannot
284
+ * change the parent itself. Cases where the parent's shape needs to
285
+ * change in response to its children — e.g. an inline `<a>` that
286
+ * encloses a block `<h1>`, which must be lifted so the heading takes
287
+ * the link's place and the link is redistributed onto the heading's
288
+ * inline contents — belong in the rule body, not the schema. See the
289
+ * "Lifting blocks out of an inline parent" section of the docs.
290
+ *
291
+ * @experimental
292
+ */
293
+ export interface ChildSchema {
294
+ /** Optional name for debug output. */
295
+ readonly name?: string;
296
+ /**
297
+ * Returns `true` if `child` is a valid child of `parent` in this position.
298
+ */
299
+ $accepts(child: LexicalNode, parent: LexicalNode | null): boolean;
300
+ /**
301
+ * Package a maximal run of non-accepted siblings into zero or more
302
+ * accepted nodes. The returned nodes replace the rejected run at the
303
+ * same position in the child list and are not re-checked against
304
+ * `$accepts` — the caller is trusted to return valid children. If
305
+ * omitted, or if it returns an empty array, {@link ChildSchema.onReject}
306
+ * is consulted instead.
307
+ */
308
+ $packageRun?(
309
+ rejected: LexicalNode[],
310
+ parent: LexicalNode | null,
311
+ domParent: Node | null,
312
+ ): LexicalNode[];
313
+ /**
314
+ * Fallback strategy for a run of non-accepted children when
315
+ * `$packageRun` is missing or returns an empty array:
316
+ *
317
+ * - `'drop'` (default) — silently discards the run. Use when the
318
+ * schema is strict and rejected content is meaningless in this
319
+ * position (e.g. text between table rows).
320
+ * - `'hoist'` — emits the rejected nodes unchanged at the same
321
+ * position in the assembled child list. The caller's parent then
322
+ * receives them as-is, which is only useful if the calling rule
323
+ * intends to surface mixed content to *its* parent (a less common
324
+ * pattern; usually `$packageRun` should re-shape the run first).
325
+ *
326
+ * `'hoist'` does NOT lift the run all the way up out of the calling
327
+ * rule's parent — that requires the rule itself to detect the
328
+ * situation and emit a different parent structure (see the
329
+ * "Lifting blocks out of an inline parent" section).
330
+ */
331
+ readonly onReject?: 'hoist' | 'drop';
332
+ /**
333
+ * Final pass over the assembled child list (after `$packageRun`). Returns
334
+ * the children to actually attach. Use to enforce structural invariants
335
+ * (e.g. drop empty runs, pad short table rows).
336
+ */
337
+ $finalize?(
338
+ children: LexicalNode[],
339
+ parent: LexicalNode | null,
340
+ ): LexicalNode[];
341
+ }
342
+
343
+ /**
344
+ * The middleware signature of an import rule. Call `$next()` to delegate to
345
+ * the next-matching rule for this node (returning its result, which may then
346
+ * be inspected or wrapped); return `[]` to drop the node.
347
+ *
348
+ * @experimental
349
+ */
350
+ export type DOMImportFn<
351
+ E extends Node,
352
+ C extends Record<string, RegExpMatchArray> = Record<string, never>,
353
+ > = (
354
+ ctx: DOMImportContext<C>,
355
+ node: E,
356
+ $next: () => readonly LexicalNode[],
357
+ ) => readonly LexicalNode[];
358
+
359
+ /**
360
+ * An importer for a DOM node, dispatched by `match` and implemented by
361
+ * `$import`.
362
+ *
363
+ * @experimental
364
+ */
365
+ export interface DOMImportRule<S extends CompiledSelector = CompiledSelector> {
366
+ /**
367
+ * Optional identifier surfaced in dev-mode logs, error messages, and
368
+ * introspection devtools. Convention: `'@scope/package/rule-id'` for
369
+ * library rules.
370
+ */
371
+ readonly name?: string;
372
+ /** A {@link CompiledSelector} produced by the {@link sel} builder. */
373
+ readonly match: S;
374
+ /** Middleware that converts the matched DOM node into lexical nodes. */
375
+ readonly $import: DOMImportFn<NodeOfSelector<S>, CapturesOfSelector<S>>;
376
+ }
377
+
378
+ /** @experimental */
379
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
380
+ export type AnyDOMImportRule = DOMImportRule<any>;
381
+
382
+ /**
383
+ * Context exposed to a {@link DOMPreprocessFn}. Lets the preprocessor:
384
+ *
385
+ * - Write to the per-import {@link ImportSession} (the same
386
+ * `ctx.session` rules see during the walk). Writes mutate the
387
+ * root-layer context record, so they are visible to every scoped
388
+ * `ctx.get(cfg)` read that hasn't been shadowed by a branch.
389
+ *
390
+ * @experimental
391
+ */
392
+ export interface DOMPreprocessContext {
393
+ /**
394
+ * Document-order-shared store, same instance as `ctx.session` in
395
+ * rules. Use to pass info from the DOM preprocess phase (e.g.
396
+ * inspected `<meta>` tags, collected `<style>` text) to the
397
+ * importer rules.
398
+ */
399
+ readonly session: ImportSession;
400
+ }
401
+
402
+ /**
403
+ * A middleware step in the DOM-preprocess chain. Runs before walking
404
+ * begins and may:
405
+ *
406
+ * - Mutate the input DOM in place (e.g. inline stylesheets, strip
407
+ * unsafe elements, normalize attributes).
408
+ * - Write to {@link DOMPreprocessContext.session} for rules to read
409
+ * (and for unshadowed scoped reads to pick up).
410
+ * - Call `$next()` to defer to the next-lower preprocessor in the
411
+ * stack; omit the call to short-circuit and skip the rest.
412
+ *
413
+ * The preprocess phase runs inside the same editor read / update
414
+ * context as the walk that follows, so a preprocess function may call
415
+ * `$`-prefixed Lexical APIs (e.g. `$getState`, `$getRoot`) as needed.
416
+ * The `$next` parameter is named with a `$` prefix to make that
417
+ * editor-context expectation visible to readers and lints.
418
+ *
419
+ * Append-style merge applies: an extension's preprocessors are appended
420
+ * to the existing stack, so later-registered preprocessors run first
421
+ * and may delegate to earlier (lower-priority) ones via `$next()`. Same
422
+ * convention as {@link ExportMimeTypeFunction} on the export side.
423
+ *
424
+ * @experimental
425
+ */
426
+ export type DOMPreprocessFn = (
427
+ dom: Document | ParentNode,
428
+ ctx: DOMPreprocessContext,
429
+ $next: () => void,
430
+ ) => void;
431
+
432
+ /**
433
+ * Per-call options to the extension's `$generateNodesFromDOM`.
434
+ *
435
+ * @experimental
436
+ */
437
+ export interface GenerateNodesFromDOMOptions {
438
+ /**
439
+ * Context pairs/updaters applied for the duration of this import only —
440
+ * use to communicate per-call info such as the {@link ImportSource}.
441
+ */
442
+ readonly context?: readonly ImportContextPairOrUpdater[];
443
+ /**
444
+ * Additional preprocessors to run on this call only, on top of the
445
+ * extension's configured {@link DOMImportConfig.preprocess}. Per-call
446
+ * preprocessors run AFTER the configured ones.
447
+ */
448
+ readonly preprocess?: readonly DOMPreprocessFn[];
449
+ }
450
+
451
+ /**
452
+ * Output of {@link DOMImportExtension}.
453
+ *
454
+ * @experimental
455
+ */
456
+ export interface DOMImportExtensionOutput {
457
+ /**
458
+ * Convert a {@link Document} or {@link ParentNode} into lexical nodes,
459
+ * using the dispatcher compiled from this extension's configured
460
+ * {@link DOMImportRule}s.
461
+ *
462
+ * Must be called within an `editor.update()` or `editor.read()` because
463
+ * the importers may invoke `$create...` helpers.
464
+ */
465
+ $generateNodesFromDOM(
466
+ dom: Document | ParentNode,
467
+ options?: GenerateNodesFromDOMOptions,
468
+ ): LexicalNode[];
469
+ /** @internal */
470
+ readonly defaults: undefined | ContextRecord<typeof DOMImportContextSymbol>;
471
+ }