@lexical/html 0.44.1-nightly.20260518.0 → 0.45.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 +3192 -0
  4. package/dist/LexicalHtml.dev.mjs +3146 -0
  5. package/{LexicalHtml.js.flow → dist/LexicalHtml.js.flow} +16 -16
  6. package/dist/LexicalHtml.mjs +56 -0
  7. package/dist/LexicalHtml.node.mjs +54 -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 +27 -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 +91 -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 +53 -0
  42. package/src/import/ImportContext.ts +339 -0
  43. package/src/import/compileImportRules.ts +178 -0
  44. package/src/import/coreImportRules.ts +485 -0
  45. package/src/import/defineImportRule.ts +40 -0
  46. package/src/import/defineOverlayRules.ts +105 -0
  47. package/src/import/index.ts +96 -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 +236 -0
  52. package/src/import/sel.ts +314 -0
  53. package/src/import/types.ts +471 -0
  54. package/src/index.ts +555 -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,314 @@
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 {
9
+ AttrMatchOptions,
10
+ CompiledSelector,
11
+ ElementSelectorBuilder,
12
+ StyleMatchOptions,
13
+ } from './types';
14
+
15
+ import invariant from '@lexical/internal/invariant';
16
+ import {isDOMTextNode, isHTMLElement} from 'lexical';
17
+
18
+ /**
19
+ * @internal
20
+ *
21
+ * A predicate that may write into the per-invocation `captures` map. Returns
22
+ * `true` if the rule matches; `false` otherwise.
23
+ */
24
+ export type Predicate = (
25
+ node: Node,
26
+ captures: Record<string, RegExpMatchArray>,
27
+ ) => boolean;
28
+
29
+ /** @internal */
30
+ export type SelectorKind = 'element' | 'text' | 'comment';
31
+
32
+ /** @internal The runtime shape of a {@link CompiledSelector}. */
33
+ export interface SelectorImpl {
34
+ readonly kind: SelectorKind;
35
+ /**
36
+ * Uppercased tag names this selector is restricted to. Empty for wildcard
37
+ * element selectors and for text / comment selectors (dispatched by
38
+ * `kind`).
39
+ */
40
+ readonly tags: ReadonlySet<string>;
41
+ /** Composed predicate run against a candidate node. */
42
+ readonly predicate: Predicate;
43
+ }
44
+
45
+ const IMPL = Symbol.for('@lexical/html/SelectorImpl');
46
+
47
+ /** @internal */
48
+ export function getSelectorImpl(sel: CompiledSelector): SelectorImpl {
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ const impl = (sel as any)[IMPL] as SelectorImpl | undefined;
51
+ invariant(
52
+ impl !== undefined,
53
+ 'match must be a CompiledSelector produced by sel.* or sel.css(); received a raw object.',
54
+ );
55
+ return impl;
56
+ }
57
+
58
+ function combinePredicates(preds: readonly Predicate[]): Predicate {
59
+ if (preds.length === 0) {
60
+ return isHTMLElement;
61
+ }
62
+ if (preds.length === 1) {
63
+ return preds[0];
64
+ }
65
+ return (node, captures) => {
66
+ for (const p of preds) {
67
+ if (!p(node, captures)) {
68
+ return false;
69
+ }
70
+ }
71
+ return true;
72
+ };
73
+ }
74
+
75
+ /**
76
+ * @internal
77
+ *
78
+ * Build a selector value from a tag set and a predicate list. Used by the
79
+ * combinator API and the CSS parser.
80
+ */
81
+ export function buildSelector(
82
+ tags: ReadonlySet<string>,
83
+ predicates: readonly Predicate[],
84
+ ): ElementSelectorBuilder<HTMLElement> {
85
+ const impl: SelectorImpl = {
86
+ kind: 'element',
87
+ predicate: combinePredicates(predicates),
88
+ tags,
89
+ };
90
+ const refine = (additional: Predicate) =>
91
+ buildSelector(tags, [...predicates, additional]);
92
+ const builder = {
93
+ [IMPL]: impl,
94
+ attr: (name: string, value: unknown, options?: AttrMatchOptions) =>
95
+ refine(buildAttrPredicate(name, value, options)),
96
+ classAll: (...classes: readonly string[]) =>
97
+ refine(buildClassAllPredicate(classes)),
98
+ classAny: (...classes: readonly string[]) =>
99
+ refine(buildClassAnyPredicate(classes)),
100
+ styleAny: (prop: string, value: unknown, options?: StyleMatchOptions) =>
101
+ refine(buildStylePredicate(prop, value, options)),
102
+ };
103
+ // The runtime is fully type-erased; cast to satisfy the surface.
104
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ return builder as any;
106
+ }
107
+
108
+ function normalizeClassList(classes: readonly string[]): readonly string[] {
109
+ const out: string[] = [];
110
+ for (const c of classes) {
111
+ if (c) {
112
+ out.push(c);
113
+ }
114
+ }
115
+ return out;
116
+ }
117
+
118
+ /** @internal */
119
+ export function buildClassAllPredicate(classes: readonly string[]): Predicate {
120
+ const ns = normalizeClassList(classes);
121
+ if (ns.length === 0) {
122
+ return () => true;
123
+ }
124
+ return node => {
125
+ if (!isHTMLElement(node)) {
126
+ return false;
127
+ }
128
+ const cl = node.classList;
129
+ for (const c of ns) {
130
+ if (!cl.contains(c)) {
131
+ return false;
132
+ }
133
+ }
134
+ return true;
135
+ };
136
+ }
137
+
138
+ /** @internal */
139
+ export function buildClassAnyPredicate(classes: readonly string[]): Predicate {
140
+ const ns = normalizeClassList(classes);
141
+ if (ns.length === 0) {
142
+ return () => false;
143
+ }
144
+ return node => {
145
+ if (!isHTMLElement(node)) {
146
+ return false;
147
+ }
148
+ const cl = node.classList;
149
+ for (const c of ns) {
150
+ if (cl.contains(c)) {
151
+ return true;
152
+ }
153
+ }
154
+ return false;
155
+ };
156
+ }
157
+
158
+ /** @internal */
159
+ export function buildAttrPredicate(
160
+ name: string,
161
+ value: unknown,
162
+ options?: AttrMatchOptions,
163
+ ): Predicate {
164
+ if (value === true) {
165
+ return node => isHTMLElement(node) && node.hasAttribute(name);
166
+ }
167
+ if (typeof value === 'string') {
168
+ return node => isHTMLElement(node) && node.getAttribute(name) === value;
169
+ }
170
+ if (value instanceof RegExp) {
171
+ const capture = options && options.capture;
172
+ const re = value;
173
+ return (node, captures) => {
174
+ if (!isHTMLElement(node)) {
175
+ return false;
176
+ }
177
+ const v = node.getAttribute(name);
178
+ if (v == null) {
179
+ return false;
180
+ }
181
+ const m = v.match(re);
182
+ if (m === null) {
183
+ return false;
184
+ }
185
+ if (capture !== undefined) {
186
+ captures[capture] = m;
187
+ }
188
+ return true;
189
+ };
190
+ }
191
+ invariant(
192
+ false,
193
+ 'sel.attr(%s, ...) requires true, a string, or a RegExp',
194
+ JSON.stringify(name),
195
+ );
196
+ }
197
+
198
+ function buildStylePredicate(
199
+ prop: string,
200
+ value: unknown,
201
+ options?: StyleMatchOptions,
202
+ ): Predicate {
203
+ if (typeof value === 'string') {
204
+ return node =>
205
+ isHTMLElement(node) && node.style.getPropertyValue(prop) === value;
206
+ }
207
+ if (value instanceof RegExp) {
208
+ const capture = options && options.capture;
209
+ const re = value;
210
+ return (node, captures) => {
211
+ if (!isHTMLElement(node)) {
212
+ return false;
213
+ }
214
+ const v = node.style.getPropertyValue(prop);
215
+ if (!v) {
216
+ return false;
217
+ }
218
+ const m = v.match(re);
219
+ if (m === null) {
220
+ return false;
221
+ }
222
+ if (capture !== undefined) {
223
+ captures[capture] = m;
224
+ }
225
+ return true;
226
+ };
227
+ }
228
+ invariant(
229
+ false,
230
+ 'sel.styleAny(%s, ...) requires a string or a RegExp',
231
+ JSON.stringify(prop),
232
+ );
233
+ }
234
+
235
+ const TEXT_SELECTOR_IMPL: SelectorImpl = {
236
+ kind: 'text',
237
+ predicate: isDOMTextNode,
238
+ tags: new Set(),
239
+ };
240
+
241
+ // The `as` cast is needed because `CompiledSelector` is an opaque
242
+ // branded interface — neither the object literal nor a typed const can
243
+ // declare the internal `IMPL` symbol without exposing it.
244
+ const TEXT_SELECTOR = {[IMPL]: TEXT_SELECTOR_IMPL} as CompiledSelector<Text>;
245
+
246
+ const COMMENT_SELECTOR_IMPL: SelectorImpl = {
247
+ kind: 'comment',
248
+ predicate: node => node.nodeType === 8 /* COMMENT_NODE */,
249
+ tags: new Set(),
250
+ };
251
+
252
+ const COMMENT_SELECTOR = {
253
+ [IMPL]: COMMENT_SELECTOR_IMPL,
254
+ } as CompiledSelector<Comment>;
255
+
256
+ /**
257
+ * Combinator API for building {@link CompiledSelector}s. The public
258
+ * `sel` is augmented from this in `./index.ts` (where the CSS parser is
259
+ * available without a circular import); consumers outside `@lexical/html`
260
+ * should always import the public `sel` from the package root.
261
+ *
262
+ * @internal
263
+ */
264
+ export const selBase = {
265
+ /** Match any {@link HTMLElement}. */
266
+ any(): ElementSelectorBuilder<HTMLElement> {
267
+ return buildSelector(new Set(), []);
268
+ },
269
+
270
+ /** Match DOM {@link Comment} nodes. */
271
+ comment(): CompiledSelector<Comment> {
272
+ return COMMENT_SELECTOR;
273
+ },
274
+
275
+ /**
276
+ * Match by tag name(s). With one literal tag the element type is narrowed
277
+ * (e.g. `'a' → HTMLAnchorElement`); with multiple, it is the union of
278
+ * their `HTMLElementTagNameMap` entries.
279
+ */
280
+ tag<const Tags extends readonly string[]>(
281
+ ...tags: Tags
282
+ ): ElementSelectorBuilder<
283
+ Tags[number] extends keyof HTMLElementTagNameMap
284
+ ? HTMLElementTagNameMap[Tags[number]]
285
+ : HTMLElement
286
+ > {
287
+ invariant(tags.length > 0, 'sel.tag() requires at least one tag name');
288
+ const upper = new Set<string>();
289
+ for (const t of tags) {
290
+ upper.add(t.toUpperCase());
291
+ }
292
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
293
+ return buildSelector(upper, []) as any;
294
+ },
295
+
296
+ /** Match DOM {@link Text} nodes. */
297
+ text(): CompiledSelector<Text> {
298
+ return TEXT_SELECTOR;
299
+ },
300
+ } as const;
301
+
302
+ /**
303
+ * Cross-frame-safe replacement for `node instanceof HTMLXxxElement`. Returns
304
+ * true when `node` is an HTMLElement whose `nodeName` equals `tag` (compared
305
+ * case-insensitively).
306
+ *
307
+ * @experimental
308
+ */
309
+ export function isElementOfTag<T extends keyof HTMLElementTagNameMap>(
310
+ node: Node,
311
+ tag: T,
312
+ ): node is HTMLElementTagNameMap[T] {
313
+ return isHTMLElement(node) && node.nodeName === tag.toUpperCase();
314
+ }