@knighted/jsx 1.5.1 → 1.5.2

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.
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createResolveAttributes = void 0;
4
+ const createResolveAttributes = (deps) => {
5
+ const { getIdentifierName, evaluateExpressionWithNamespace } = deps;
6
+ return (attributes, ctx, namespace) => {
7
+ const props = {};
8
+ attributes.forEach(attribute => {
9
+ if (attribute.type === 'JSXSpreadAttribute') {
10
+ const spreadValue = evaluateExpressionWithNamespace(attribute.argument, ctx, namespace);
11
+ if (spreadValue &&
12
+ typeof spreadValue === 'object' &&
13
+ !Array.isArray(spreadValue)) {
14
+ Object.assign(props, spreadValue);
15
+ }
16
+ return;
17
+ }
18
+ const name = getIdentifierName(attribute.name);
19
+ if (!attribute.value) {
20
+ props[name] = true;
21
+ return;
22
+ }
23
+ if (attribute.value.type === 'Literal') {
24
+ props[name] = attribute.value.value;
25
+ return;
26
+ }
27
+ if (attribute.value.type === 'JSXExpressionContainer') {
28
+ if (attribute.value.expression.type === 'JSXEmptyExpression') {
29
+ return;
30
+ }
31
+ props[name] = evaluateExpressionWithNamespace(attribute.value.expression, ctx, namespace);
32
+ }
33
+ });
34
+ return props;
35
+ };
36
+ };
37
+ exports.createResolveAttributes = createResolveAttributes;
@@ -0,0 +1,10 @@
1
+ import type { Expression, JSXAttribute, JSXElement, JSXFragment, JSXSpreadAttribute } from '@oxc-project/types';
2
+ import type { TemplateComponent, TemplateContext } from '../runtime/shared.cjs';
3
+ export type Namespace = 'svg' | null;
4
+ export type EvaluateExpressionWithNamespace<TComponent extends TemplateComponent> = (expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>, namespace: Namespace) => unknown;
5
+ export type ResolveAttributesDependencies<TComponent extends TemplateComponent> = {
6
+ getIdentifierName: (name: JSXAttribute['name']) => string;
7
+ evaluateExpressionWithNamespace: EvaluateExpressionWithNamespace<TComponent>;
8
+ };
9
+ export type ResolveAttributesFn<TComponent extends TemplateComponent> = (attributes: (JSXAttribute | JSXSpreadAttribute)[], ctx: TemplateContext<TComponent>, namespace: Namespace) => Record<string, unknown>;
10
+ export declare const createResolveAttributes: <TComponent extends TemplateComponent>(deps: ResolveAttributesDependencies<TComponent>) => ResolveAttributesFn<TComponent>;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveEventHandlerValue = exports.parseEventPropName = void 0;
4
+ const captureSuffix = 'Capture';
5
+ const stripCaptureSuffix = (rawName) => {
6
+ if (rawName.endsWith(captureSuffix)) {
7
+ return { eventName: rawName.slice(0, -captureSuffix.length), capture: true };
8
+ }
9
+ return { eventName: rawName, capture: false };
10
+ };
11
+ const parseEventPropName = (name) => {
12
+ if (!name.startsWith('on')) {
13
+ return null;
14
+ }
15
+ if (name.startsWith('on:')) {
16
+ const raw = name.slice(3);
17
+ if (!raw) {
18
+ return null;
19
+ }
20
+ const parsed = stripCaptureSuffix(raw);
21
+ if (!parsed.eventName) {
22
+ return null;
23
+ }
24
+ return parsed;
25
+ }
26
+ const raw = name.slice(2);
27
+ if (!raw) {
28
+ return null;
29
+ }
30
+ const parsed = stripCaptureSuffix(raw);
31
+ if (!parsed.eventName) {
32
+ return null;
33
+ }
34
+ return {
35
+ eventName: parsed.eventName.toLowerCase(),
36
+ capture: parsed.capture,
37
+ };
38
+ };
39
+ exports.parseEventPropName = parseEventPropName;
40
+ const isEventListenerObject = (value) => {
41
+ if (!value || typeof value !== 'object') {
42
+ return false;
43
+ }
44
+ return ('handleEvent' in value &&
45
+ typeof value.handleEvent === 'function');
46
+ };
47
+ const isEventHandlerDescriptor = (value) => {
48
+ if (!value || typeof value !== 'object' || !('handler' in value)) {
49
+ return false;
50
+ }
51
+ const handler = value.handler;
52
+ if (typeof handler === 'function') {
53
+ return true;
54
+ }
55
+ return isEventListenerObject(handler);
56
+ };
57
+ const resolveEventHandlerValue = (value) => {
58
+ if (typeof value === 'function' || isEventListenerObject(value)) {
59
+ return { listener: value };
60
+ }
61
+ if (!isEventHandlerDescriptor(value)) {
62
+ return null;
63
+ }
64
+ const descriptor = value;
65
+ let options = descriptor.options ? { ...descriptor.options } : undefined;
66
+ const assignOption = (key, optionValue) => {
67
+ if (optionValue === undefined || optionValue === null) {
68
+ return;
69
+ }
70
+ if (!options) {
71
+ options = {};
72
+ }
73
+ options[key] = optionValue;
74
+ };
75
+ assignOption('capture', descriptor.capture);
76
+ assignOption('once', descriptor.once);
77
+ assignOption('passive', descriptor.passive);
78
+ assignOption('signal', descriptor.signal ?? undefined);
79
+ return {
80
+ listener: descriptor.handler,
81
+ options,
82
+ };
83
+ };
84
+ exports.resolveEventHandlerValue = resolveEventHandlerValue;
@@ -0,0 +1,18 @@
1
+ export type ParsedEventBinding = {
2
+ eventName: string;
3
+ capture: boolean;
4
+ };
5
+ export declare const parseEventPropName: (name: string) => ParsedEventBinding | null;
6
+ export type EventHandlerDescriptor = {
7
+ handler: EventListenerOrEventListenerObject;
8
+ capture?: boolean;
9
+ once?: boolean;
10
+ passive?: boolean;
11
+ signal?: AbortSignal | null;
12
+ options?: AddEventListenerOptions;
13
+ };
14
+ export type ResolvedEventHandler = {
15
+ listener: EventListenerOrEventListenerObject;
16
+ options?: AddEventListenerOptions;
17
+ };
18
+ export declare const resolveEventHandlerValue: (value: unknown) => ResolvedEventHandler | null;
package/dist/cjs/jsx.cjs CHANGED
@@ -4,6 +4,8 @@ exports.jsx = void 0;
4
4
  const oxc_parser_1 = require("oxc-parser");
5
5
  const shared_js_1 = require("./runtime/shared.cjs");
6
6
  const property_information_1 = require("property-information");
7
+ const attribute_resolution_js_1 = require("./internal/attribute-resolution.cjs");
8
+ const event_bindings_js_1 = require("./internal/event-bindings.cjs");
7
9
  const ensureDomAvailable = () => {
8
10
  if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
9
11
  throw new Error('The jsx template tag requires a DOM-like environment (document missing).');
@@ -71,85 +73,6 @@ const shouldAssignProperty = (element, namespace, info) => {
71
73
  }
72
74
  return info.property in element;
73
75
  };
74
- const captureSuffix = 'Capture';
75
- const stripCaptureSuffix = (rawName) => {
76
- if (rawName.endsWith(captureSuffix) && rawName.length > captureSuffix.length) {
77
- return { eventName: rawName.slice(0, -captureSuffix.length), capture: true };
78
- }
79
- return { eventName: rawName, capture: false };
80
- };
81
- const parseEventPropName = (name) => {
82
- if (!name.startsWith('on')) {
83
- return null;
84
- }
85
- if (name.startsWith('on:')) {
86
- const raw = name.slice(3);
87
- if (!raw) {
88
- return null;
89
- }
90
- const parsed = stripCaptureSuffix(raw);
91
- if (!parsed.eventName) {
92
- return null;
93
- }
94
- return parsed;
95
- }
96
- const raw = name.slice(2);
97
- if (!raw) {
98
- return null;
99
- }
100
- const parsed = stripCaptureSuffix(raw);
101
- if (!parsed.eventName) {
102
- return null;
103
- }
104
- return {
105
- eventName: parsed.eventName.toLowerCase(),
106
- capture: parsed.capture,
107
- };
108
- };
109
- const isEventListenerObject = (value) => {
110
- if (!value || typeof value !== 'object') {
111
- return false;
112
- }
113
- return ('handleEvent' in value &&
114
- typeof value.handleEvent === 'function');
115
- };
116
- const isEventHandlerDescriptor = (value) => {
117
- if (!value || typeof value !== 'object' || !('handler' in value)) {
118
- return false;
119
- }
120
- const handler = value.handler;
121
- if (typeof handler === 'function') {
122
- return true;
123
- }
124
- return isEventListenerObject(handler);
125
- };
126
- const resolveEventHandlerValue = (value) => {
127
- if (typeof value === 'function' || isEventListenerObject(value)) {
128
- return { listener: value };
129
- }
130
- if (!isEventHandlerDescriptor(value)) {
131
- return null;
132
- }
133
- const descriptor = value;
134
- let options = descriptor.options ? { ...descriptor.options } : undefined;
135
- const assignOption = (key, optionValue) => {
136
- if (optionValue === undefined || optionValue === null) {
137
- return;
138
- }
139
- if (!options) {
140
- options = {};
141
- }
142
- options[key] = optionValue;
143
- };
144
- assignOption('capture', descriptor.capture);
145
- assignOption('once', descriptor.once);
146
- assignOption('passive', descriptor.passive);
147
- assignOption('signal', descriptor.signal ?? undefined);
148
- return {
149
- listener: descriptor.handler,
150
- options,
151
- };
152
- };
153
76
  const setDomProp = (element, name, value, namespace) => {
154
77
  if (value === null || value === undefined) {
155
78
  return;
@@ -191,9 +114,9 @@ const setDomProp = (element, name, value, namespace) => {
191
114
  });
192
115
  return;
193
116
  }
194
- const eventBinding = parseEventPropName(name);
117
+ const eventBinding = (0, event_bindings_js_1.parseEventPropName)(name);
195
118
  if (eventBinding) {
196
- const handlerValue = resolveEventHandlerValue(value);
119
+ const handlerValue = (0, event_bindings_js_1.resolveEventHandlerValue)(value);
197
120
  if (handlerValue) {
198
121
  let options = handlerValue.options ? { ...handlerValue.options } : undefined;
199
122
  if (eventBinding.capture) {
@@ -288,34 +211,10 @@ const appendChildValue = (parent, value) => {
288
211
  parent.appendChild(document.createTextNode(String(value)));
289
212
  };
290
213
  const evaluateExpressionWithNamespace = (expression, ctx, namespace) => (0, shared_js_1.evaluateExpression)(expression, ctx, node => evaluateJsxNode(node, ctx, namespace));
291
- const resolveAttributes = (attributes, ctx, namespace) => {
292
- const props = {};
293
- attributes.forEach(attribute => {
294
- if (attribute.type === 'JSXSpreadAttribute') {
295
- const spreadValue = evaluateExpressionWithNamespace(attribute.argument, ctx, namespace);
296
- if (spreadValue && typeof spreadValue === 'object' && !Array.isArray(spreadValue)) {
297
- Object.assign(props, spreadValue);
298
- }
299
- return;
300
- }
301
- const name = (0, shared_js_1.getIdentifierName)(attribute.name);
302
- if (!attribute.value) {
303
- props[name] = true;
304
- return;
305
- }
306
- if (attribute.value.type === 'Literal') {
307
- props[name] = attribute.value.value;
308
- return;
309
- }
310
- if (attribute.value.type === 'JSXExpressionContainer') {
311
- if (attribute.value.expression.type === 'JSXEmptyExpression') {
312
- return;
313
- }
314
- props[name] = evaluateExpressionWithNamespace(attribute.value.expression, ctx, namespace);
315
- }
316
- });
317
- return props;
318
- };
214
+ const resolveAttributes = (0, attribute_resolution_js_1.createResolveAttributes)({
215
+ getIdentifierName: shared_js_1.getIdentifierName,
216
+ evaluateExpressionWithNamespace,
217
+ });
319
218
  const applyDomAttributes = (element, attributes, ctx, namespace) => {
320
219
  const props = resolveAttributes(attributes, ctx, namespace);
321
220
  Object.entries(props).forEach(([name, value]) => {
@@ -165,6 +165,9 @@ class ReactTemplateBuilder {
165
165
  throw new Error('[jsx-loader] Unable to inline complex expressions in react mode.');
166
166
  }
167
167
  /* c8 ignore next */
168
+ /* v8 ignore next */
169
+ /* istanbul ignore next */
170
+ // Should never happen because OXC always annotates expression ranges.
168
171
  throw new Error('[jsx-loader] Unable to compile expression for react mode.');
169
172
  }
170
173
  buildCreateElement(type, props, children) {
@@ -276,11 +279,15 @@ const shouldInterpolateName = (name) => /^[A-Z]/.test(name.name);
276
279
  const addSlot = (slots, source, range) => {
277
280
  if (!range) {
278
281
  /* c8 ignore next */
282
+ /* v8 ignore next */
283
+ // OXC always provides ranges; guard defends against malformed AST nodes.
279
284
  return;
280
285
  }
281
286
  const [start, end] = range;
282
287
  if (start === end) {
283
288
  /* c8 ignore next */
289
+ /* v8 ignore next */
290
+ // Zero-length ranges indicate parser bugs and would emit empty slices.
284
291
  return;
285
292
  }
286
293
  slots.push({
@@ -293,6 +300,9 @@ const collectSlots = (program, source) => {
293
300
  const slots = [];
294
301
  const recordComponentName = (name) => {
295
302
  if (!name) {
303
+ /* c8 ignore next */
304
+ /* v8 ignore next */
305
+ // JSX elements emitted by OXC always carry a name; this is defensive.
296
306
  return;
297
307
  }
298
308
  switch (name.type) {
@@ -362,6 +372,8 @@ const renderTemplateWithSlots = (source, slots) => {
362
372
  slots.forEach(slot => {
363
373
  if (slot.start < cursor) {
364
374
  /* c8 ignore next */
375
+ /* v8 ignore next */
376
+ // Slots are generated from non-overlapping JSX ranges; this protects against parser regressions.
365
377
  throw new Error('Overlapping JSX expressions detected inside template literal.');
366
378
  }
367
379
  output += escapeTemplateChunk(source.slice(cursor, slot.start));
@@ -469,6 +481,8 @@ const buildTemplateSource = (quasis, expressions, source, tag) => {
469
481
  let chunk = quasi.value.cooked;
470
482
  if (typeof chunk !== 'string') {
471
483
  /* c8 ignore next */
484
+ /* v8 ignore next */
485
+ // Cooked text is always available for valid templates; fall back shields invalid escape sequences.
472
486
  chunk = quasi.value.raw ?? '';
473
487
  }
474
488
  if (trimStartNext > 0) {
@@ -484,6 +498,8 @@ const buildTemplateSource = (quasis, expressions, source, tag) => {
484
498
  const end = expression.end ?? null;
485
499
  if (start === null || end === null) {
486
500
  /* c8 ignore next */
501
+ /* v8 ignore next */
502
+ // Expressions parsed from tagged templates always include start/end ranges.
487
503
  throw new Error('Unable to read template expression source range.');
488
504
  }
489
505
  const nextChunk = quasis[index + 1];
@@ -554,6 +570,8 @@ const isLoaderPlaceholderIdentifier = (node) => {
554
570
  (node.type !== 'Identifier' && node.type !== 'JSXIdentifier') ||
555
571
  typeof node.name !== 'string') {
556
572
  /* c8 ignore next */
573
+ /* v8 ignore next */
574
+ // Visitor only calls this helper with identifier-like nodes; guard prevents crashes on malformed ASTs.
557
575
  return false;
558
576
  }
559
577
  return (node.name.startsWith(TEMPLATE_EXPR_PLACEHOLDER_PREFIX) ||
@@ -605,6 +623,8 @@ const transformSource = (source, config) => {
605
623
  return;
606
624
  }
607
625
  /* c8 ignore next */
626
+ /* v8 ignore next */
627
+ // Modes are validated during option parsing; this fallback guards future extensions.
608
628
  throw new Error(`[jsx-loader] Transformation mode "${mode}" not implemented yet for tag "${tagName}".`);
609
629
  });
610
630
  return {
@@ -1,3 +1,7 @@
1
+ import { createRequire } from 'node:module';
2
+ const nodeRequire = createRequire(import.meta.url);
3
+ let requireOverride = null;
4
+ const resolveRequire = () => requireOverride ?? nodeRequire;
1
5
  const DOM_TEMPLATE = '<!doctype html><html><body></body></html>';
2
6
  const GLOBAL_KEYS = [
3
7
  'window',
@@ -23,13 +27,13 @@ const assignGlobalTargets = (windowObj) => {
23
27
  }
24
28
  });
25
29
  };
26
- const loadLinkedom = async () => {
27
- const { parseHTML } = await import('linkedom');
30
+ const loadLinkedom = () => {
31
+ const { parseHTML } = resolveRequire()('linkedom');
28
32
  const { window } = parseHTML(DOM_TEMPLATE);
29
33
  return window;
30
34
  };
31
- const loadJsdom = async () => {
32
- const { JSDOM } = await import('jsdom');
35
+ const loadJsdom = () => {
36
+ const { JSDOM } = resolveRequire()('jsdom');
33
37
  const { window } = new JSDOM(DOM_TEMPLATE);
34
38
  return window;
35
39
  };
@@ -52,11 +56,11 @@ const selectLoaders = () => {
52
56
  }
53
57
  return [loadLinkedom, loadJsdom];
54
58
  };
55
- const createShimWindow = async () => {
59
+ const createShimWindow = () => {
56
60
  const errors = [];
57
61
  for (const loader of selectLoaders()) {
58
62
  try {
59
- return await loader();
63
+ return loader();
60
64
  }
61
65
  catch (error) {
62
66
  errors.push(error);
@@ -65,19 +69,15 @@ const createShimWindow = async () => {
65
69
  const help = 'Unable to bootstrap a DOM-like environment. Install "linkedom" or "jsdom" (both optional peer dependencies) or set KNIGHTED_JSX_NODE_SHIM to pick one explicitly.';
66
70
  throw new AggregateError(errors, help);
67
71
  };
68
- let bootstrapPromise = null;
69
- export const ensureNodeDom = async () => {
70
- if (hasDom()) {
72
+ let bootstrapped = false;
73
+ export const ensureNodeDom = () => {
74
+ if (hasDom() || bootstrapped) {
71
75
  return;
72
76
  }
73
- if (!bootstrapPromise) {
74
- bootstrapPromise = (async () => {
75
- const windowObj = await createShimWindow();
76
- assignGlobalTargets(windowObj);
77
- })().catch(error => {
78
- bootstrapPromise = null;
79
- throw error;
80
- });
81
- }
82
- return bootstrapPromise;
77
+ const windowObj = createShimWindow();
78
+ assignGlobalTargets(windowObj);
79
+ bootstrapped = true;
80
+ };
81
+ export const __setNodeRequireForTesting = (mockRequire) => {
82
+ requireOverride = mockRequire;
83
83
  };
@@ -1 +1,2 @@
1
- export declare const ensureNodeDom: () => Promise<void>;
1
+ export declare const ensureNodeDom: () => void;
2
+ export declare const __setNodeRequireForTesting: (mockRequire: NodeJS.Require | null) => void;
@@ -1,4 +1,4 @@
1
1
  import { ensureNodeDom } from './bootstrap.cjs';
2
2
  import { jsx as baseJsx } from '../jsx.cjs';
3
- await ensureNodeDom();
3
+ ensureNodeDom();
4
4
  export const jsx = baseJsx;
@@ -0,0 +1,10 @@
1
+ import type { Expression, JSXAttribute, JSXElement, JSXFragment, JSXSpreadAttribute } from '@oxc-project/types';
2
+ import type { TemplateComponent, TemplateContext } from '../runtime/shared.js';
3
+ export type Namespace = 'svg' | null;
4
+ export type EvaluateExpressionWithNamespace<TComponent extends TemplateComponent> = (expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>, namespace: Namespace) => unknown;
5
+ export type ResolveAttributesDependencies<TComponent extends TemplateComponent> = {
6
+ getIdentifierName: (name: JSXAttribute['name']) => string;
7
+ evaluateExpressionWithNamespace: EvaluateExpressionWithNamespace<TComponent>;
8
+ };
9
+ export type ResolveAttributesFn<TComponent extends TemplateComponent> = (attributes: (JSXAttribute | JSXSpreadAttribute)[], ctx: TemplateContext<TComponent>, namespace: Namespace) => Record<string, unknown>;
10
+ export declare const createResolveAttributes: <TComponent extends TemplateComponent>(deps: ResolveAttributesDependencies<TComponent>) => ResolveAttributesFn<TComponent>;
@@ -0,0 +1,33 @@
1
+ export const createResolveAttributes = (deps) => {
2
+ const { getIdentifierName, evaluateExpressionWithNamespace } = deps;
3
+ return (attributes, ctx, namespace) => {
4
+ const props = {};
5
+ attributes.forEach(attribute => {
6
+ if (attribute.type === 'JSXSpreadAttribute') {
7
+ const spreadValue = evaluateExpressionWithNamespace(attribute.argument, ctx, namespace);
8
+ if (spreadValue &&
9
+ typeof spreadValue === 'object' &&
10
+ !Array.isArray(spreadValue)) {
11
+ Object.assign(props, spreadValue);
12
+ }
13
+ return;
14
+ }
15
+ const name = getIdentifierName(attribute.name);
16
+ if (!attribute.value) {
17
+ props[name] = true;
18
+ return;
19
+ }
20
+ if (attribute.value.type === 'Literal') {
21
+ props[name] = attribute.value.value;
22
+ return;
23
+ }
24
+ if (attribute.value.type === 'JSXExpressionContainer') {
25
+ if (attribute.value.expression.type === 'JSXEmptyExpression') {
26
+ return;
27
+ }
28
+ props[name] = evaluateExpressionWithNamespace(attribute.value.expression, ctx, namespace);
29
+ }
30
+ });
31
+ return props;
32
+ };
33
+ };
@@ -0,0 +1,18 @@
1
+ export type ParsedEventBinding = {
2
+ eventName: string;
3
+ capture: boolean;
4
+ };
5
+ export declare const parseEventPropName: (name: string) => ParsedEventBinding | null;
6
+ export type EventHandlerDescriptor = {
7
+ handler: EventListenerOrEventListenerObject;
8
+ capture?: boolean;
9
+ once?: boolean;
10
+ passive?: boolean;
11
+ signal?: AbortSignal | null;
12
+ options?: AddEventListenerOptions;
13
+ };
14
+ export type ResolvedEventHandler = {
15
+ listener: EventListenerOrEventListenerObject;
16
+ options?: AddEventListenerOptions;
17
+ };
18
+ export declare const resolveEventHandlerValue: (value: unknown) => ResolvedEventHandler | null;
@@ -0,0 +1,79 @@
1
+ const captureSuffix = 'Capture';
2
+ const stripCaptureSuffix = (rawName) => {
3
+ if (rawName.endsWith(captureSuffix)) {
4
+ return { eventName: rawName.slice(0, -captureSuffix.length), capture: true };
5
+ }
6
+ return { eventName: rawName, capture: false };
7
+ };
8
+ export const parseEventPropName = (name) => {
9
+ if (!name.startsWith('on')) {
10
+ return null;
11
+ }
12
+ if (name.startsWith('on:')) {
13
+ const raw = name.slice(3);
14
+ if (!raw) {
15
+ return null;
16
+ }
17
+ const parsed = stripCaptureSuffix(raw);
18
+ if (!parsed.eventName) {
19
+ return null;
20
+ }
21
+ return parsed;
22
+ }
23
+ const raw = name.slice(2);
24
+ if (!raw) {
25
+ return null;
26
+ }
27
+ const parsed = stripCaptureSuffix(raw);
28
+ if (!parsed.eventName) {
29
+ return null;
30
+ }
31
+ return {
32
+ eventName: parsed.eventName.toLowerCase(),
33
+ capture: parsed.capture,
34
+ };
35
+ };
36
+ const isEventListenerObject = (value) => {
37
+ if (!value || typeof value !== 'object') {
38
+ return false;
39
+ }
40
+ return ('handleEvent' in value &&
41
+ typeof value.handleEvent === 'function');
42
+ };
43
+ const isEventHandlerDescriptor = (value) => {
44
+ if (!value || typeof value !== 'object' || !('handler' in value)) {
45
+ return false;
46
+ }
47
+ const handler = value.handler;
48
+ if (typeof handler === 'function') {
49
+ return true;
50
+ }
51
+ return isEventListenerObject(handler);
52
+ };
53
+ export const resolveEventHandlerValue = (value) => {
54
+ if (typeof value === 'function' || isEventListenerObject(value)) {
55
+ return { listener: value };
56
+ }
57
+ if (!isEventHandlerDescriptor(value)) {
58
+ return null;
59
+ }
60
+ const descriptor = value;
61
+ let options = descriptor.options ? { ...descriptor.options } : undefined;
62
+ const assignOption = (key, optionValue) => {
63
+ if (optionValue === undefined || optionValue === null) {
64
+ return;
65
+ }
66
+ if (!options) {
67
+ options = {};
68
+ }
69
+ options[key] = optionValue;
70
+ };
71
+ assignOption('capture', descriptor.capture);
72
+ assignOption('once', descriptor.once);
73
+ assignOption('passive', descriptor.passive);
74
+ assignOption('signal', descriptor.signal ?? undefined);
75
+ return {
76
+ listener: descriptor.handler,
77
+ options,
78
+ };
79
+ };
package/dist/jsx.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { parseSync } from 'oxc-parser';
2
2
  import { buildTemplate, evaluateExpression, extractRootNode, formatParserError, getIdentifierName, normalizeJsxTextSegments, parserOptions, } from './runtime/shared.js';
3
3
  import { find as findPropertyInfo, html as htmlProperties, svg as svgProperties, } from 'property-information';
4
+ import { createResolveAttributes, } from './internal/attribute-resolution.js';
5
+ import { parseEventPropName, resolveEventHandlerValue, } from './internal/event-bindings.js';
4
6
  const ensureDomAvailable = () => {
5
7
  if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
6
8
  throw new Error('The jsx template tag requires a DOM-like environment (document missing).');
@@ -68,85 +70,6 @@ const shouldAssignProperty = (element, namespace, info) => {
68
70
  }
69
71
  return info.property in element;
70
72
  };
71
- const captureSuffix = 'Capture';
72
- const stripCaptureSuffix = (rawName) => {
73
- if (rawName.endsWith(captureSuffix) && rawName.length > captureSuffix.length) {
74
- return { eventName: rawName.slice(0, -captureSuffix.length), capture: true };
75
- }
76
- return { eventName: rawName, capture: false };
77
- };
78
- const parseEventPropName = (name) => {
79
- if (!name.startsWith('on')) {
80
- return null;
81
- }
82
- if (name.startsWith('on:')) {
83
- const raw = name.slice(3);
84
- if (!raw) {
85
- return null;
86
- }
87
- const parsed = stripCaptureSuffix(raw);
88
- if (!parsed.eventName) {
89
- return null;
90
- }
91
- return parsed;
92
- }
93
- const raw = name.slice(2);
94
- if (!raw) {
95
- return null;
96
- }
97
- const parsed = stripCaptureSuffix(raw);
98
- if (!parsed.eventName) {
99
- return null;
100
- }
101
- return {
102
- eventName: parsed.eventName.toLowerCase(),
103
- capture: parsed.capture,
104
- };
105
- };
106
- const isEventListenerObject = (value) => {
107
- if (!value || typeof value !== 'object') {
108
- return false;
109
- }
110
- return ('handleEvent' in value &&
111
- typeof value.handleEvent === 'function');
112
- };
113
- const isEventHandlerDescriptor = (value) => {
114
- if (!value || typeof value !== 'object' || !('handler' in value)) {
115
- return false;
116
- }
117
- const handler = value.handler;
118
- if (typeof handler === 'function') {
119
- return true;
120
- }
121
- return isEventListenerObject(handler);
122
- };
123
- const resolveEventHandlerValue = (value) => {
124
- if (typeof value === 'function' || isEventListenerObject(value)) {
125
- return { listener: value };
126
- }
127
- if (!isEventHandlerDescriptor(value)) {
128
- return null;
129
- }
130
- const descriptor = value;
131
- let options = descriptor.options ? { ...descriptor.options } : undefined;
132
- const assignOption = (key, optionValue) => {
133
- if (optionValue === undefined || optionValue === null) {
134
- return;
135
- }
136
- if (!options) {
137
- options = {};
138
- }
139
- options[key] = optionValue;
140
- };
141
- assignOption('capture', descriptor.capture);
142
- assignOption('once', descriptor.once);
143
- assignOption('passive', descriptor.passive);
144
- assignOption('signal', descriptor.signal ?? undefined);
145
- return {
146
- listener: descriptor.handler,
147
- options,
148
- };
149
- };
150
73
  const setDomProp = (element, name, value, namespace) => {
151
74
  if (value === null || value === undefined) {
152
75
  return;
@@ -285,34 +208,10 @@ const appendChildValue = (parent, value) => {
285
208
  parent.appendChild(document.createTextNode(String(value)));
286
209
  };
287
210
  const evaluateExpressionWithNamespace = (expression, ctx, namespace) => evaluateExpression(expression, ctx, node => evaluateJsxNode(node, ctx, namespace));
288
- const resolveAttributes = (attributes, ctx, namespace) => {
289
- const props = {};
290
- attributes.forEach(attribute => {
291
- if (attribute.type === 'JSXSpreadAttribute') {
292
- const spreadValue = evaluateExpressionWithNamespace(attribute.argument, ctx, namespace);
293
- if (spreadValue && typeof spreadValue === 'object' && !Array.isArray(spreadValue)) {
294
- Object.assign(props, spreadValue);
295
- }
296
- return;
297
- }
298
- const name = getIdentifierName(attribute.name);
299
- if (!attribute.value) {
300
- props[name] = true;
301
- return;
302
- }
303
- if (attribute.value.type === 'Literal') {
304
- props[name] = attribute.value.value;
305
- return;
306
- }
307
- if (attribute.value.type === 'JSXExpressionContainer') {
308
- if (attribute.value.expression.type === 'JSXEmptyExpression') {
309
- return;
310
- }
311
- props[name] = evaluateExpressionWithNamespace(attribute.value.expression, ctx, namespace);
312
- }
313
- });
314
- return props;
315
- };
211
+ const resolveAttributes = createResolveAttributes({
212
+ getIdentifierName,
213
+ evaluateExpressionWithNamespace,
214
+ });
316
215
  const applyDomAttributes = (element, attributes, ctx, namespace) => {
317
216
  const props = resolveAttributes(attributes, ctx, namespace);
318
217
  Object.entries(props).forEach(([name, value]) => {
@@ -1,4 +1,4 @@
1
- import {parseSync}from'oxc-parser';import {find,svg,html}from'property-information';var F=/<\s*$/,B=/<\/\s*$/,T="__KX_EXPR__",C=new RegExp(`${T}\\d+_\\d+__`,"g"),M=0,N={lang:"jsx",sourceType:"module",range:true,preserveParens:true},A=e=>{let n=`[oxc-parser] ${e.message}`;if(e.labels?.length){let t=e.labels[0];t.message&&(n+=`
2
- ${t.message}`);}return e.codeframe&&(n+=`
3
- ${e.codeframe}`),n},k=e=>{for(let n of e.body)if(n.type==="ExpressionStatement"){let t=n.expression;if(t.type==="JSXElement"||t.type==="JSXFragment")return t}throw new Error("The jsx template must contain a single JSX element or fragment.")},y=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${y(e.object)}.${e.property.name}`;default:return ""}},E=(e,n)=>{if(!e||typeof e!="object")return;let t=e;typeof t.type=="string"&&(n(t),Object.values(t).forEach(r=>{if(r){if(Array.isArray(r)){r.forEach(o=>E(o,n));return}typeof r=="object"&&E(r,n);}}));},X=(e,n)=>{let t=e.replace(/\r/g,"").replace(/\n\s+/g," "),r=e.match(/^\s*/)?.[0]??"",o=e.match(/\s*$/)?.[0]??"",s=/\n/.test(r),a=/\n/.test(o),c=t;if(s&&(c=c.replace(/^\s+/,"")),a&&(c=c.replace(/\s+$/,"")),c.length===0||c.trim().length===0)return [];let i=[];C.lastIndex=0;let p=0,l;for(;l=C.exec(c);){let u=l.index,m=c.slice(p,u);m&&i.push(m);let f=l[0];n.has(f)?i.push(n.get(f)):i.push(f),p=u+f.length;}let d=c.slice(p);return d&&i.push(d),i},D=(e,n)=>{let t=new Set;return E(e,r=>{r.type==="Identifier"&&n.placeholders.has(r.name)&&t.add(r.name);}),Array.from(t)},P=(e,n,t)=>{if(e.type==="JSXElement"||e.type==="JSXFragment")return t(e);if(!("range"in e)||!e.range)throw new Error("Unable to evaluate expression: missing source range information.");let[r,o]=e.range,s=n.source.slice(r,o),a=D(e,n);try{let c=new Function(...a,`"use strict"; return (${s});`),i=a.map(p=>n.placeholders.get(p));return c(...i)}catch(c){throw new Error(`Failed to evaluate expression ${s}: ${c.message}`)}},W=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},H=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,s=W(o??""),a=s,c=1;for(;n.some(p=>p.name===a);)a=`${s}${c++}`;let i={name:a,value:e};return n.push(i),t.set(e,i),i},R=(e,n)=>{let t=e.raw??e,r=new Map,o=[],s=new Map,a=t[0]??"",c=M++,i=0;for(let p=0;p<n.length;p++){let l=t[p]??"",d=t[p+1]??"",u=n[p],m=F.test(l)||B.test(l);if(m&&typeof u=="function"){let $=H(u,o,s);a+=$.name+d;continue}if(m&&typeof u=="string"){a+=u+d;continue}let f=`${T}${c}_${i++}__`;r.set(f,u),a+=f+d;}return {source:a,placeholders:r,bindings:o}};var Z=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},G=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,q=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",v=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",Q={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},I=e=>{if(!(!e||e==="html"||e==="svg"))return Q[e]},Y=e=>e==="svg"?svg:html,h=(e,n,t)=>{let r=I(n.space),o=String(t);if(r){e.setAttributeNS(r,n.attribute,o);return}e.setAttribute(n.attribute,o);},j=(e,n)=>{let t=I(n.space);if(t){e.removeAttributeNS(t,n.attribute);return}e.removeAttribute(n.attribute);},b=(e,n)=>Array.isArray(e)?e.filter(Boolean).join(n):e,ee=(e,n,t)=>n==="svg"||!t.property||t.property.includes(":")?false:t.property in e,S="Capture",O=e=>e.endsWith(S)&&e.length>S.length?{eventName:e.slice(0,-S.length),capture:true}:{eventName:e,capture:false},ne=e=>{if(!e.startsWith("on"))return null;if(e.startsWith("on:")){let r=e.slice(3);if(!r)return null;let o=O(r);return o.eventName?o:null}let n=e.slice(2);if(!n)return null;let t=O(n);return t.eventName?{eventName:t.eventName.toLowerCase(),capture:t.capture}:null},L=e=>!e||typeof e!="object"?false:"handleEvent"in e&&typeof e.handleEvent=="function",te=e=>{if(!e||typeof e!="object"||!("handler"in e))return false;let n=e.handler;return typeof n=="function"?true:L(n)},re=e=>{if(typeof e=="function"||L(e))return {listener:e};if(!te(e))return null;let n=e,t=n.options?{...n.options}:void 0,r=(o,s)=>{s!=null&&(t||(t={}),t[o]=s);};return r("capture",n.capture),r("once",n.once),r("passive",n.passive),r("signal",n.signal??void 0),{listener:n.handler,options:t}},oe=(e,n,t,r)=>{if(t==null)return;if(n==="dangerouslySetInnerHTML"&&typeof t=="object"&&t&&"__html"in t){e.innerHTML=String(t.__html??"");return}if(n==="ref"){if(typeof t=="function"){t(e);return}if(t&&typeof t=="object"){t.current=e;return}}if(n==="style"&&typeof t=="object"&&t!==null){let p=t,l=e.style;if(!l)return;let d=l;Object.entries(p).forEach(([u,m])=>{if(m!=null){if(u.startsWith("--")){l.setProperty(u,String(m));return}d[u]=m;}});return}let o=ne(n);if(o){let p=re(t);if(p){let l=p.options?{...p.options}:void 0;o.capture&&(l?l.capture=true:l={capture:true}),e.addEventListener(o.eventName,p.listener,l);return}}let s=find(Y(r),n),a=e,c=ee(e,r,s);if(s.mustUseProperty){let p=s.boolean?!!t:t;a[s.property]=p;return}if(s.boolean){let p=!!t;c&&(a[s.property]=p),p?h(e,s,""):j(e,s);return}let i=t;if(s.spaceSeparated?i=b(t," "):s.commaSeparated?i=b(t,","):s.commaOrSpaceSeparated&&(i=b(t," ")),s.booleanish&&typeof i=="boolean"&&(i=i?"true":"false"),s.overloadedBoolean){if(i===false){j(e,s);return}if(i===true){h(e,s,"");return}}if(c){a[s.property]=i;return}i!==false&&h(e,s,i);},g=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(v(n))throw new Error("Async values are not supported inside jsx template results.");if(Array.isArray(n)){n.forEach(t=>g(e,t));return}if(q(n)){for(let t of n)g(e,t);return}if(G(n)){e.appendChild(n);return}e.appendChild(document.createTextNode(String(n)));}},x=(e,n,t)=>P(e,n,r=>w(r,n,t)),_=(e,n,t)=>{let r={};return e.forEach(o=>{if(o.type==="JSXSpreadAttribute"){let a=x(o.argument,n,t);a&&typeof a=="object"&&!Array.isArray(a)&&Object.assign(r,a);return}let s=y(o.name);if(!o.value){r[s]=true;return}if(o.value.type==="Literal"){r[s]=o.value.value;return}if(o.value.type==="JSXExpressionContainer"){if(o.value.expression.type==="JSXEmptyExpression")return;r[s]=x(o.value.expression,n,t);}}),r},se=(e,n,t,r)=>{let o=_(n,t,r);Object.entries(o).forEach(([s,a])=>{if(s!=="key"){if(s==="children"){g(e,a);return}oe(e,s,a,r);}});},J=(e,n,t)=>{let r=[];return e.forEach(o=>{switch(o.type){case "JSXText":{X(o.value,n.placeholders).forEach(a=>{r.push(a);});break}case "JSXExpressionContainer":{if(o.expression.type==="JSXEmptyExpression")break;r.push(x(o.expression,n,t));break}case "JSXSpreadChild":{let s=x(o.expression,n,t);s!=null&&r.push(s);break}case "JSXElement":case "JSXFragment":{r.push(w(o,n,t));break}}}),r},ae=(e,n,t,r)=>{let o=_(e.openingElement.attributes,n,r),s=J(e.children,n,r);s.length===1?o.children=s[0]:s.length>1&&(o.children=s);let a=t(o);if(v(a))throw new Error("Async jsx components are not supported.");return a},ie=(e,n,t)=>{let r=e.openingElement,o=y(r.name),s=n.components.get(o);if(s)return ae(e,n,s,t);if(/[A-Z]/.test(o[0]??""))throw new Error(`Unknown component "${o}". Did you interpolate it with the template literal?`);let a=o==="svg"?"svg":t,c=o==="foreignObject"?null:a,i=a==="svg"?document.createElementNS("http://www.w3.org/2000/svg",o):document.createElement(o);return se(i,r.attributes,n,a),J(e.children,n,c).forEach(l=>g(i,l)),i},w=(e,n,t)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return J(e.children,n,t).forEach(s=>g(r,s)),r}return ie(e,n,t)},pe=(e,...n)=>{Z();let t=R(e,n),r=parseSync("inline.jsx",t.source,N);if(r.errors.length>0)throw new Error(A(r.errors[0]));let o=k(r.program),s={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(a=>[a.name,a.value]))};return w(o,s,null)};
4
- export{pe as jsx};
1
+ import {parseSync}from'oxc-parser';import {find,svg,html}from'property-information';var M=/<\s*$/,W=/<\/\s*$/,w="__KX_EXPR__",C=new RegExp(`${w}\\d+_\\d+__`,"g"),H=0,T={lang:"jsx",sourceType:"module",range:true,preserveParens:true},N=e=>{let t=`[oxc-parser] ${e.message}`;if(e.labels?.length){let n=e.labels[0];n.message&&(t+=`
2
+ ${n.message}`);}return e.codeframe&&(t+=`
3
+ ${e.codeframe}`),t},A=e=>{for(let t of e.body)if(t.type==="ExpressionStatement"){let n=t.expression;if(n.type==="JSXElement"||n.type==="JSXFragment")return n}throw new Error("The jsx template must contain a single JSX element or fragment.")},x=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${x(e.object)}.${e.property.name}`;default:return ""}},y=(e,t)=>{if(!e||typeof e!="object")return;let n=e;typeof n.type=="string"&&(t(n),Object.values(n).forEach(r=>{if(r){if(Array.isArray(r)){r.forEach(s=>y(s,t));return}typeof r=="object"&&y(r,t);}}));},X=(e,t)=>{let n=e.replace(/\r/g,"").replace(/\n\s+/g," "),r=e.match(/^\s*/)?.[0]??"",s=e.match(/\s*$/)?.[0]??"",o=/\n/.test(r),a=/\n/.test(s),p=n;if(o&&(p=p.replace(/^\s+/,"")),a&&(p=p.replace(/\s+$/,"")),p.length===0||p.trim().length===0)return [];let i=[];C.lastIndex=0;let c=0,l;for(;l=C.exec(p);){let m=l.index,u=p.slice(c,m);u&&i.push(u);let f=l[0];t.has(f)?i.push(t.get(f)):i.push(f),c=m+f.length;}let d=p.slice(c);return d&&i.push(d),i},V=(e,t)=>{let n=new Set;return y(e,r=>{r.type==="Identifier"&&t.placeholders.has(r.name)&&n.add(r.name);}),Array.from(n)},k=(e,t,n)=>{if(e.type==="JSXElement"||e.type==="JSXFragment")return n(e);if(!("range"in e)||!e.range)throw new Error("Unable to evaluate expression: missing source range information.");let[r,s]=e.range,o=t.source.slice(r,s),a=V(e,t);try{let p=new Function(...a,`"use strict"; return (${o});`),i=a.map(c=>t.placeholders.get(c));return p(...i)}catch(p){throw new Error(`Failed to evaluate expression ${o}: ${p.message}`)}},z=e=>{let t=e.replace(/[^a-zA-Z0-9_$]/g,"");return t?/[A-Za-z_$]/.test(t[0])?t:`Component${t}`:"Component"},U=(e,t,n)=>{let r=n.get(e);if(r)return r;let s=e.displayName||e.name||`Component${t.length}`,o=z(s??""),a=o,p=1;for(;t.some(c=>c.name===a);)a=`${o}${p++}`;let i={name:a,value:e};return t.push(i),n.set(e,i),i},R=(e,t)=>{let n=e.raw??e,r=new Map,s=[],o=new Map,a=n[0]??"",p=H++,i=0;for(let c=0;c<t.length;c++){let l=n[c]??"",d=n[c+1]??"",m=t[c],u=M.test(l)||W.test(l);if(u&&typeof m=="function"){let D=U(m,s,o);a+=D.name+d;continue}if(u&&typeof m=="string"){a+=m+d;continue}let f=`${w}${p}_${i++}__`;r.set(f,m),a+=f+d;}return {source:a,placeholders:r,bindings:s}};var v=e=>{let{getIdentifierName:t,evaluateExpressionWithNamespace:n}=e;return (r,s,o)=>{let a={};return r.forEach(p=>{if(p.type==="JSXSpreadAttribute"){let c=n(p.argument,s,o);c&&typeof c=="object"&&!Array.isArray(c)&&Object.assign(a,c);return}let i=t(p.name);if(!p.value){a[i]=true;return}if(p.value.type==="Literal"){a[i]=p.value.value;return}if(p.value.type==="JSXExpressionContainer"){if(p.value.expression.type==="JSXEmptyExpression")return;a[i]=n(p.value.expression,s,o);}}),a}};var P="Capture",j=e=>e.endsWith(P)?{eventName:e.slice(0,-P.length),capture:true}:{eventName:e,capture:false},O=e=>{if(!e.startsWith("on"))return null;if(e.startsWith("on:")){let r=e.slice(3);if(!r)return null;let s=j(r);return s.eventName?s:null}let t=e.slice(2);if(!t)return null;let n=j(t);return n.eventName?{eventName:n.eventName.toLowerCase(),capture:n.capture}:null},I=e=>!e||typeof e!="object"?false:"handleEvent"in e&&typeof e.handleEvent=="function",K=e=>{if(!e||typeof e!="object"||!("handler"in e))return false;let t=e.handler;return typeof t=="function"?true:I(t)},F=e=>{if(typeof e=="function"||I(e))return {listener:e};if(!K(e))return null;let t=e,n=t.options?{...t.options}:void 0,r=(s,o)=>{o!=null&&(n||(n={}),n[s]=o);};return r("capture",t.capture),r("once",t.once),r("passive",t.passive),r("signal",t.signal??void 0),{listener:t.handler,options:n}};var Y=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},ee=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,te=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",_=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",ne={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},$=e=>{if(!(!e||e==="html"||e==="svg"))return ne[e]},re=e=>e==="svg"?svg:html,E=(e,t,n)=>{let r=$(t.space),s=String(n);if(r){e.setAttributeNS(r,t.attribute,s);return}e.setAttribute(t.attribute,s);},L=(e,t)=>{let n=$(t.space);if(n){e.removeAttributeNS(n,t.attribute);return}e.removeAttribute(t.attribute);},h=(e,t)=>Array.isArray(e)?e.filter(Boolean).join(t):e,oe=(e,t,n)=>t==="svg"||!n.property||n.property.includes(":")?false:n.property in e,se=(e,t,n,r)=>{if(n==null)return;if(t==="dangerouslySetInnerHTML"&&typeof n=="object"&&n&&"__html"in n){e.innerHTML=String(n.__html??"");return}if(t==="ref"){if(typeof n=="function"){n(e);return}if(n&&typeof n=="object"){n.current=e;return}}if(t==="style"&&typeof n=="object"&&n!==null){let c=n,l=e.style;if(!l)return;let d=l;Object.entries(c).forEach(([m,u])=>{if(u!=null){if(m.startsWith("--")){l.setProperty(m,String(u));return}d[m]=u;}});return}let s=O(t);if(s){let c=F(n);if(c){let l=c.options?{...c.options}:void 0;s.capture&&(l?l.capture=true:l={capture:true}),e.addEventListener(s.eventName,c.listener,l);return}}let o=find(re(r),t),a=e,p=oe(e,r,o);if(o.mustUseProperty){let c=o.boolean?!!n:n;a[o.property]=c;return}if(o.boolean){let c=!!n;p&&(a[o.property]=c),c?E(e,o,""):L(e,o);return}let i=n;if(o.spaceSeparated?i=h(n," "):o.commaSeparated?i=h(n,","):o.commaOrSpaceSeparated&&(i=h(n," ")),o.booleanish&&typeof i=="boolean"&&(i=i?"true":"false"),o.overloadedBoolean){if(i===false){L(e,o);return}if(i===true){E(e,o,"");return}}if(p){a[o.property]=i;return}i!==false&&E(e,o,i);},g=(e,t)=>{if(t!=null&&typeof t!="boolean"){if(_(t))throw new Error("Async values are not supported inside jsx template results.");if(Array.isArray(t)){t.forEach(n=>g(e,n));return}if(te(t)){for(let n of t)g(e,n);return}if(ee(t)){e.appendChild(t);return}e.appendChild(document.createTextNode(String(t)));}},b=(e,t,n)=>k(e,t,r=>J(r,t,n)),B=v({getIdentifierName:x,evaluateExpressionWithNamespace:b}),ae=(e,t,n,r)=>{let s=B(t,n,r);Object.entries(s).forEach(([o,a])=>{if(o!=="key"){if(o==="children"){g(e,a);return}se(e,o,a,r);}});},S=(e,t,n)=>{let r=[];return e.forEach(s=>{switch(s.type){case "JSXText":{X(s.value,t.placeholders).forEach(a=>{r.push(a);});break}case "JSXExpressionContainer":{if(s.expression.type==="JSXEmptyExpression")break;r.push(b(s.expression,t,n));break}case "JSXSpreadChild":{let o=b(s.expression,t,n);o!=null&&r.push(o);break}case "JSXElement":case "JSXFragment":{r.push(J(s,t,n));break}}}),r},ie=(e,t,n,r)=>{let s=B(e.openingElement.attributes,t,r),o=S(e.children,t,r);o.length===1?s.children=o[0]:o.length>1&&(s.children=o);let a=n(s);if(_(a))throw new Error("Async jsx components are not supported.");return a},pe=(e,t,n)=>{let r=e.openingElement,s=x(r.name),o=t.components.get(s);if(o)return ie(e,t,o,n);if(/[A-Z]/.test(s[0]??""))throw new Error(`Unknown component "${s}". Did you interpolate it with the template literal?`);let a=s==="svg"?"svg":n,p=s==="foreignObject"?null:a,i=a==="svg"?document.createElementNS("http://www.w3.org/2000/svg",s):document.createElement(s);return ae(i,r.attributes,t,a),S(e.children,t,p).forEach(l=>g(i,l)),i},J=(e,t,n)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return S(e.children,t,n).forEach(o=>g(r,o)),r}return pe(e,t,n)},ce=(e,...t)=>{Y();let n=R(e,t),r=parseSync("inline.jsx",n.source,T);if(r.errors.length>0)throw new Error(N(r.errors[0]));let s=A(r.program),o={source:n.source,placeholders:n.placeholders,components:new Map(n.bindings.map(a=>[a.name,a.value]))};return J(s,o,null)};
4
+ export{ce as jsx};
@@ -1,4 +1,4 @@
1
- import {parseSync}from'oxc-parser';import {find,svg,html}from'property-information';var k="<!doctype html><html><body></body></html>",V=["window","self","document","HTMLElement","Element","Node","DocumentFragment","customElements","Text","Comment","MutationObserver","navigator"],K=()=>typeof document<"u"&&typeof document.createElement=="function",U=e=>{let n=globalThis,t=e;V.forEach(r=>{n[r]===void 0&&t[r]!==void 0&&(n[r]=t[r]);});},E=async()=>{let{parseHTML:e}=await import('linkedom'),{window:n}=e(k);return n},S=async()=>{let{JSDOM:e}=await import('jsdom'),{window:n}=new e(k);return n},z=()=>{let e=typeof process<"u"&&process.env?.KNIGHTED_JSX_NODE_SHIM?process.env.KNIGHTED_JSX_NODE_SHIM.toLowerCase():void 0;return e==="linkedom"||e==="jsdom"?e:"auto"},G=()=>{let e=z();return e==="linkedom"?[E,S]:e==="jsdom"?[S,E]:[E,S]},Z=async()=>{let e=[];for(let t of G())try{return await t()}catch(r){e.push(r);}let n='Unable to bootstrap a DOM-like environment. Install "linkedom" or "jsdom" (both optional peer dependencies) or set KNIGHTED_JSX_NODE_SHIM to pick one explicitly.';throw new AggregateError(e,n)},y=null,A=async()=>{if(!K())return y||(y=(async()=>{let e=await Z();U(e);})().catch(e=>{throw y=null,e})),y};var q=/<\s*$/,Y=/<\/\s*$/,P="__KX_EXPR__",X=new RegExp(`${P}\\d+_\\d+__`,"g"),Q=0,O={lang:"jsx",sourceType:"module",range:true,preserveParens:true},R=e=>{let n=`[oxc-parser] ${e.message}`;if(e.labels?.length){let t=e.labels[0];t.message&&(n+=`
2
- ${t.message}`);}return e.codeframe&&(n+=`
3
- ${e.codeframe}`),n},j=e=>{for(let n of e.body)if(n.type==="ExpressionStatement"){let t=n.expression;if(t.type==="JSXElement"||t.type==="JSXFragment")return t}throw new Error("The jsx template must contain a single JSX element or fragment.")},h=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${h(e.object)}.${e.property.name}`;default:return ""}},w=(e,n)=>{if(!e||typeof e!="object")return;let t=e;typeof t.type=="string"&&(n(t),Object.values(t).forEach(r=>{if(r){if(Array.isArray(r)){r.forEach(o=>w(o,n));return}typeof r=="object"&&w(r,n);}}));},v=(e,n)=>{let t=e.replace(/\r/g,"").replace(/\n\s+/g," "),r=e.match(/^\s*/)?.[0]??"",o=e.match(/\s*$/)?.[0]??"",s=/\n/.test(r),i=/\n/.test(o),p=t;if(s&&(p=p.replace(/^\s+/,"")),i&&(p=p.replace(/\s+$/,"")),p.length===0||p.trim().length===0)return [];let a=[];X.lastIndex=0;let c=0,l;for(;l=X.exec(p);){let m=l.index,u=p.slice(c,m);u&&a.push(u);let f=l[0];n.has(f)?a.push(n.get(f)):a.push(f),c=m+f.length;}let d=p.slice(c);return d&&a.push(d),a},ee=(e,n)=>{let t=new Set;return w(e,r=>{r.type==="Identifier"&&n.placeholders.has(r.name)&&t.add(r.name);}),Array.from(t)},L=(e,n,t)=>{if(e.type==="JSXElement"||e.type==="JSXFragment")return t(e);if(!("range"in e)||!e.range)throw new Error("Unable to evaluate expression: missing source range information.");let[r,o]=e.range,s=n.source.slice(r,o),i=ee(e,n);try{let p=new Function(...i,`"use strict"; return (${s});`),a=i.map(c=>n.placeholders.get(c));return p(...a)}catch(p){throw new Error(`Failed to evaluate expression ${s}: ${p.message}`)}},ne=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},te=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,s=ne(o??""),i=s,p=1;for(;n.some(c=>c.name===i);)i=`${s}${p++}`;let a={name:i,value:e};return n.push(a),t.set(e,a),a},_=(e,n)=>{let t=e.raw??e,r=new Map,o=[],s=new Map,i=t[0]??"",p=Q++,a=0;for(let c=0;c<n.length;c++){let l=t[c]??"",d=t[c+1]??"",m=n[c],u=q.test(l)||Y.test(l);if(u&&typeof m=="function"){let W=te(m,o,s);i+=W.name+d;continue}if(u&&typeof m=="string"){i+=m+d;continue}let f=`${P}${p}_${a++}__`;r.set(f,m),i+=f+d;}return {source:i,placeholders:r,bindings:o}};var ae=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},ce=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,pe=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",M=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",le={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},$=e=>{if(!(!e||e==="html"||e==="svg"))return le[e]},me=e=>e==="svg"?svg:html,b=(e,n,t)=>{let r=$(n.space),o=String(t);if(r){e.setAttributeNS(r,n.attribute,o);return}e.setAttribute(n.attribute,o);},I=(e,n)=>{let t=$(n.space);if(t){e.removeAttributeNS(t,n.attribute);return}e.removeAttribute(n.attribute);},J=(e,n)=>Array.isArray(e)?e.filter(Boolean).join(n):e,ue=(e,n,t)=>n==="svg"||!t.property||t.property.includes(":")?false:t.property in e,C="Capture",D=e=>e.endsWith(C)&&e.length>C.length?{eventName:e.slice(0,-C.length),capture:true}:{eventName:e,capture:false},de=e=>{if(!e.startsWith("on"))return null;if(e.startsWith("on:")){let r=e.slice(3);if(!r)return null;let o=D(r);return o.eventName?o:null}let n=e.slice(2);if(!n)return null;let t=D(n);return t.eventName?{eventName:t.eventName.toLowerCase(),capture:t.capture}:null},F=e=>!e||typeof e!="object"?false:"handleEvent"in e&&typeof e.handleEvent=="function",fe=e=>{if(!e||typeof e!="object"||!("handler"in e))return false;let n=e.handler;return typeof n=="function"?true:F(n)},ge=e=>{if(typeof e=="function"||F(e))return {listener:e};if(!fe(e))return null;let n=e,t=n.options?{...n.options}:void 0,r=(o,s)=>{s!=null&&(t||(t={}),t[o]=s);};return r("capture",n.capture),r("once",n.once),r("passive",n.passive),r("signal",n.signal??void 0),{listener:n.handler,options:t}},ye=(e,n,t,r)=>{if(t==null)return;if(n==="dangerouslySetInnerHTML"&&typeof t=="object"&&t&&"__html"in t){e.innerHTML=String(t.__html??"");return}if(n==="ref"){if(typeof t=="function"){t(e);return}if(t&&typeof t=="object"){t.current=e;return}}if(n==="style"&&typeof t=="object"&&t!==null){let c=t,l=e.style;if(!l)return;let d=l;Object.entries(c).forEach(([m,u])=>{if(u!=null){if(m.startsWith("--")){l.setProperty(m,String(u));return}d[m]=u;}});return}let o=de(n);if(o){let c=ge(t);if(c){let l=c.options?{...c.options}:void 0;o.capture&&(l?l.capture=true:l={capture:true}),e.addEventListener(o.eventName,c.listener,l);return}}let s=find(me(r),n),i=e,p=ue(e,r,s);if(s.mustUseProperty){let c=s.boolean?!!t:t;i[s.property]=c;return}if(s.boolean){let c=!!t;p&&(i[s.property]=c),c?b(e,s,""):I(e,s);return}let a=t;if(s.spaceSeparated?a=J(t," "):s.commaSeparated?a=J(t,","):s.commaOrSpaceSeparated&&(a=J(t," ")),s.booleanish&&typeof a=="boolean"&&(a=a?"true":"false"),s.overloadedBoolean){if(a===false){I(e,s);return}if(a===true){b(e,s,"");return}}if(p){i[s.property]=a;return}a!==false&&b(e,s,a);},g=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(M(n))throw new Error("Async values are not supported inside jsx template results.");if(Array.isArray(n)){n.forEach(t=>g(e,t));return}if(pe(n)){for(let t of n)g(e,t);return}if(ce(n)){e.appendChild(n);return}e.appendChild(document.createTextNode(String(n)));}},x=(e,n,t)=>L(e,n,r=>N(r,n,t)),B=(e,n,t)=>{let r={};return e.forEach(o=>{if(o.type==="JSXSpreadAttribute"){let i=x(o.argument,n,t);i&&typeof i=="object"&&!Array.isArray(i)&&Object.assign(r,i);return}let s=h(o.name);if(!o.value){r[s]=true;return}if(o.value.type==="Literal"){r[s]=o.value.value;return}if(o.value.type==="JSXExpressionContainer"){if(o.value.expression.type==="JSXEmptyExpression")return;r[s]=x(o.value.expression,n,t);}}),r},he=(e,n,t,r)=>{let o=B(n,t,r);Object.entries(o).forEach(([s,i])=>{if(s!=="key"){if(s==="children"){g(e,i);return}ye(e,s,i,r);}});},T=(e,n,t)=>{let r=[];return e.forEach(o=>{switch(o.type){case "JSXText":{v(o.value,n.placeholders).forEach(i=>{r.push(i);});break}case "JSXExpressionContainer":{if(o.expression.type==="JSXEmptyExpression")break;r.push(x(o.expression,n,t));break}case "JSXSpreadChild":{let s=x(o.expression,n,t);s!=null&&r.push(s);break}case "JSXElement":case "JSXFragment":{r.push(N(o,n,t));break}}}),r},xe=(e,n,t,r)=>{let o=B(e.openingElement.attributes,n,r),s=T(e.children,n,r);s.length===1?o.children=s[0]:s.length>1&&(o.children=s);let i=t(o);if(M(i))throw new Error("Async jsx components are not supported.");return i},Ee=(e,n,t)=>{let r=e.openingElement,o=h(r.name),s=n.components.get(o);if(s)return xe(e,n,s,t);if(/[A-Z]/.test(o[0]??""))throw new Error(`Unknown component "${o}". Did you interpolate it with the template literal?`);let i=o==="svg"?"svg":t,p=o==="foreignObject"?null:i,a=i==="svg"?document.createElementNS("http://www.w3.org/2000/svg",o):document.createElement(o);return he(a,r.attributes,n,i),T(e.children,n,p).forEach(l=>g(a,l)),a},N=(e,n,t)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return T(e.children,n,t).forEach(s=>g(r,s)),r}return Ee(e,n,t)},H=(e,...n)=>{ae();let t=_(e,n),r=parseSync("inline.jsx",t.source,O);if(r.errors.length>0)throw new Error(R(r.errors[0]));let o=j(r.program),s={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(i=>[i.name,i.value]))};return N(o,s,null)};await A();var Ae=H;
4
- export{Ae as jsx};
1
+ import {createRequire}from'module';import {parseSync}from'oxc-parser';import {find,svg,html}from'property-information';var G=createRequire(import.meta.url),N=()=>G,k="<!doctype html><html><body></body></html>",Y=["window","self","document","HTMLElement","Element","Node","DocumentFragment","customElements","Text","Comment","MutationObserver","navigator"],Q=()=>typeof document<"u"&&typeof document.createElement=="function",ee=e=>{let t=globalThis,n=e;Y.forEach(r=>{t[r]===void 0&&n[r]!==void 0&&(t[r]=n[r]);});},y=()=>{let{parseHTML:e}=N()("linkedom"),{window:t}=e(k);return t},E=()=>{let{JSDOM:e}=N()("jsdom"),{window:t}=new e(k);return t},te=()=>{let e=typeof process<"u"&&process.env?.KNIGHTED_JSX_NODE_SHIM?process.env.KNIGHTED_JSX_NODE_SHIM.toLowerCase():void 0;return e==="linkedom"||e==="jsdom"?e:"auto"},ne=()=>{let e=te();return e==="linkedom"?[y,E]:e==="jsdom"?[E,y]:[y,E]},re=()=>{let e=[];for(let n of ne())try{return n()}catch(r){e.push(r);}let t='Unable to bootstrap a DOM-like environment. Install "linkedom" or "jsdom" (both optional peer dependencies) or set KNIGHTED_JSX_NODE_SHIM to pick one explicitly.';throw new AggregateError(e,t)},T=false,A=()=>{if(Q()||T)return;let e=re();ee(e),T=true;};var oe=/<\s*$/,se=/<\/\s*$/,R="__KX_EXPR__",X=new RegExp(`${R}\\d+_\\d+__`,"g"),ie=0,v={lang:"jsx",sourceType:"module",range:true,preserveParens:true},j=e=>{let t=`[oxc-parser] ${e.message}`;if(e.labels?.length){let n=e.labels[0];n.message&&(t+=`
2
+ ${n.message}`);}return e.codeframe&&(t+=`
3
+ ${e.codeframe}`),t},P=e=>{for(let t of e.body)if(t.type==="ExpressionStatement"){let n=t.expression;if(n.type==="JSXElement"||n.type==="JSXFragment")return n}throw new Error("The jsx template must contain a single JSX element or fragment.")},x=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${x(e.object)}.${e.property.name}`;default:return ""}},h=(e,t)=>{if(!e||typeof e!="object")return;let n=e;typeof n.type=="string"&&(t(n),Object.values(n).forEach(r=>{if(r){if(Array.isArray(r)){r.forEach(s=>h(s,t));return}typeof r=="object"&&h(r,t);}}));},O=(e,t)=>{let n=e.replace(/\r/g,"").replace(/\n\s+/g," "),r=e.match(/^\s*/)?.[0]??"",s=e.match(/\s*$/)?.[0]??"",o=/\n/.test(r),i=/\n/.test(s),p=n;if(o&&(p=p.replace(/^\s+/,"")),i&&(p=p.replace(/\s+$/,"")),p.length===0||p.trim().length===0)return [];let a=[];X.lastIndex=0;let c=0,l;for(;l=X.exec(p);){let m=l.index,u=p.slice(c,m);u&&a.push(u);let f=l[0];t.has(f)?a.push(t.get(f)):a.push(f),c=m+f.length;}let d=p.slice(c);return d&&a.push(d),a},ae=(e,t)=>{let n=new Set;return h(e,r=>{r.type==="Identifier"&&t.placeholders.has(r.name)&&n.add(r.name);}),Array.from(n)},L=(e,t,n)=>{if(e.type==="JSXElement"||e.type==="JSXFragment")return n(e);if(!("range"in e)||!e.range)throw new Error("Unable to evaluate expression: missing source range information.");let[r,s]=e.range,o=t.source.slice(r,s),i=ae(e,t);try{let p=new Function(...i,`"use strict"; return (${o});`),a=i.map(c=>t.placeholders.get(c));return p(...a)}catch(p){throw new Error(`Failed to evaluate expression ${o}: ${p.message}`)}},pe=e=>{let t=e.replace(/[^a-zA-Z0-9_$]/g,"");return t?/[A-Za-z_$]/.test(t[0])?t:`Component${t}`:"Component"},ce=(e,t,n)=>{let r=n.get(e);if(r)return r;let s=e.displayName||e.name||`Component${t.length}`,o=pe(s??""),i=o,p=1;for(;t.some(c=>c.name===i);)i=`${o}${p++}`;let a={name:i,value:e};return t.push(a),n.set(e,a),a},_=(e,t)=>{let n=e.raw??e,r=new Map,s=[],o=new Map,i=n[0]??"",p=ie++,a=0;for(let c=0;c<t.length;c++){let l=n[c]??"",d=n[c+1]??"",m=t[c],u=oe.test(l)||se.test(l);if(u&&typeof m=="function"){let U=ce(m,s,o);i+=U.name+d;continue}if(u&&typeof m=="string"){i+=m+d;continue}let f=`${R}${p}_${a++}__`;r.set(f,m),i+=f+d;}return {source:i,placeholders:r,bindings:s}};var I=e=>{let{getIdentifierName:t,evaluateExpressionWithNamespace:n}=e;return (r,s,o)=>{let i={};return r.forEach(p=>{if(p.type==="JSXSpreadAttribute"){let c=n(p.argument,s,o);c&&typeof c=="object"&&!Array.isArray(c)&&Object.assign(i,c);return}let a=t(p.name);if(!p.value){i[a]=true;return}if(p.value.type==="Literal"){i[a]=p.value.value;return}if(p.value.type==="JSXExpressionContainer"){if(p.value.expression.type==="JSXEmptyExpression")return;i[a]=n(p.value.expression,s,o);}}),i}};var M="Capture",D=e=>e.endsWith(M)?{eventName:e.slice(0,-M.length),capture:true}:{eventName:e,capture:false},F=e=>{if(!e.startsWith("on"))return null;if(e.startsWith("on:")){let r=e.slice(3);if(!r)return null;let s=D(r);return s.eventName?s:null}let t=e.slice(2);if(!t)return null;let n=D(t);return n.eventName?{eventName:n.eventName.toLowerCase(),capture:n.capture}:null},$=e=>!e||typeof e!="object"?false:"handleEvent"in e&&typeof e.handleEvent=="function",le=e=>{if(!e||typeof e!="object"||!("handler"in e))return false;let t=e.handler;return typeof t=="function"?true:$(t)},W=e=>{if(typeof e=="function"||$(e))return {listener:e};if(!le(e))return null;let t=e,n=t.options?{...t.options}:void 0,r=(s,o)=>{o!=null&&(n||(n={}),n[s]=o);};return r("capture",t.capture),r("once",t.once),r("passive",t.passive),r("signal",t.signal??void 0),{listener:t.handler,options:n}};var ge=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},xe=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,ye=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",H=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",Ee={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},V=e=>{if(!(!e||e==="html"||e==="svg"))return Ee[e]},he=e=>e==="svg"?svg:html,S=(e,t,n)=>{let r=V(t.space),s=String(n);if(r){e.setAttributeNS(r,t.attribute,s);return}e.setAttribute(t.attribute,s);},B=(e,t)=>{let n=V(t.space);if(n){e.removeAttributeNS(n,t.attribute);return}e.removeAttribute(t.attribute);},b=(e,t)=>Array.isArray(e)?e.filter(Boolean).join(t):e,Se=(e,t,n)=>t==="svg"||!n.property||n.property.includes(":")?false:n.property in e,be=(e,t,n,r)=>{if(n==null)return;if(t==="dangerouslySetInnerHTML"&&typeof n=="object"&&n&&"__html"in n){e.innerHTML=String(n.__html??"");return}if(t==="ref"){if(typeof n=="function"){n(e);return}if(n&&typeof n=="object"){n.current=e;return}}if(t==="style"&&typeof n=="object"&&n!==null){let c=n,l=e.style;if(!l)return;let d=l;Object.entries(c).forEach(([m,u])=>{if(u!=null){if(m.startsWith("--")){l.setProperty(m,String(u));return}d[m]=u;}});return}let s=F(t);if(s){let c=W(n);if(c){let l=c.options?{...c.options}:void 0;s.capture&&(l?l.capture=true:l={capture:true}),e.addEventListener(s.eventName,c.listener,l);return}}let o=find(he(r),t),i=e,p=Se(e,r,o);if(o.mustUseProperty){let c=o.boolean?!!n:n;i[o.property]=c;return}if(o.boolean){let c=!!n;p&&(i[o.property]=c),c?S(e,o,""):B(e,o);return}let a=n;if(o.spaceSeparated?a=b(n," "):o.commaSeparated?a=b(n,","):o.commaOrSpaceSeparated&&(a=b(n," ")),o.booleanish&&typeof a=="boolean"&&(a=a?"true":"false"),o.overloadedBoolean){if(a===false){B(e,o);return}if(a===true){S(e,o,"");return}}if(p){i[o.property]=a;return}a!==false&&S(e,o,a);},g=(e,t)=>{if(t!=null&&typeof t!="boolean"){if(H(t))throw new Error("Async values are not supported inside jsx template results.");if(Array.isArray(t)){t.forEach(n=>g(e,n));return}if(ye(t)){for(let n of t)g(e,n);return}if(xe(t)){e.appendChild(t);return}e.appendChild(document.createTextNode(String(t)));}},w=(e,t,n)=>L(e,t,r=>C(r,t,n)),q=I({getIdentifierName:x,evaluateExpressionWithNamespace:w}),we=(e,t,n,r)=>{let s=q(t,n,r);Object.entries(s).forEach(([o,i])=>{if(o!=="key"){if(o==="children"){g(e,i);return}be(e,o,i,r);}});},J=(e,t,n)=>{let r=[];return e.forEach(s=>{switch(s.type){case "JSXText":{O(s.value,t.placeholders).forEach(i=>{r.push(i);});break}case "JSXExpressionContainer":{if(s.expression.type==="JSXEmptyExpression")break;r.push(w(s.expression,t,n));break}case "JSXSpreadChild":{let o=w(s.expression,t,n);o!=null&&r.push(o);break}case "JSXElement":case "JSXFragment":{r.push(C(s,t,n));break}}}),r},Je=(e,t,n,r)=>{let s=q(e.openingElement.attributes,t,r),o=J(e.children,t,r);o.length===1?s.children=o[0]:o.length>1&&(s.children=o);let i=n(s);if(H(i))throw new Error("Async jsx components are not supported.");return i},Ce=(e,t,n)=>{let r=e.openingElement,s=x(r.name),o=t.components.get(s);if(o)return Je(e,t,o,n);if(/[A-Z]/.test(s[0]??""))throw new Error(`Unknown component "${s}". Did you interpolate it with the template literal?`);let i=s==="svg"?"svg":n,p=s==="foreignObject"?null:i,a=i==="svg"?document.createElementNS("http://www.w3.org/2000/svg",s):document.createElement(s);return we(a,r.attributes,t,i),J(e.children,t,p).forEach(l=>g(a,l)),a},C=(e,t,n)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return J(e.children,t,n).forEach(o=>g(r,o)),r}return Ce(e,t,n)},K=(e,...t)=>{ge();let n=_(e,t),r=parseSync("inline.jsx",n.source,v);if(r.errors.length>0)throw new Error(j(r.errors[0]));let s=P(r.program),o={source:n.source,placeholders:n.placeholders,components:new Map(n.bindings.map(i=>[i.name,i.value]))};return C(s,o,null)};A();var Me=K;
4
+ export{Me as jsx};
@@ -159,6 +159,9 @@ class ReactTemplateBuilder {
159
159
  throw new Error('[jsx-loader] Unable to inline complex expressions in react mode.');
160
160
  }
161
161
  /* c8 ignore next */
162
+ /* v8 ignore next */
163
+ /* istanbul ignore next */
164
+ // Should never happen because OXC always annotates expression ranges.
162
165
  throw new Error('[jsx-loader] Unable to compile expression for react mode.');
163
166
  }
164
167
  buildCreateElement(type, props, children) {
@@ -270,11 +273,15 @@ const shouldInterpolateName = (name) => /^[A-Z]/.test(name.name);
270
273
  const addSlot = (slots, source, range) => {
271
274
  if (!range) {
272
275
  /* c8 ignore next */
276
+ /* v8 ignore next */
277
+ // OXC always provides ranges; guard defends against malformed AST nodes.
273
278
  return;
274
279
  }
275
280
  const [start, end] = range;
276
281
  if (start === end) {
277
282
  /* c8 ignore next */
283
+ /* v8 ignore next */
284
+ // Zero-length ranges indicate parser bugs and would emit empty slices.
278
285
  return;
279
286
  }
280
287
  slots.push({
@@ -287,6 +294,9 @@ const collectSlots = (program, source) => {
287
294
  const slots = [];
288
295
  const recordComponentName = (name) => {
289
296
  if (!name) {
297
+ /* c8 ignore next */
298
+ /* v8 ignore next */
299
+ // JSX elements emitted by OXC always carry a name; this is defensive.
290
300
  return;
291
301
  }
292
302
  switch (name.type) {
@@ -356,6 +366,8 @@ const renderTemplateWithSlots = (source, slots) => {
356
366
  slots.forEach(slot => {
357
367
  if (slot.start < cursor) {
358
368
  /* c8 ignore next */
369
+ /* v8 ignore next */
370
+ // Slots are generated from non-overlapping JSX ranges; this protects against parser regressions.
359
371
  throw new Error('Overlapping JSX expressions detected inside template literal.');
360
372
  }
361
373
  output += escapeTemplateChunk(source.slice(cursor, slot.start));
@@ -463,6 +475,8 @@ const buildTemplateSource = (quasis, expressions, source, tag) => {
463
475
  let chunk = quasi.value.cooked;
464
476
  if (typeof chunk !== 'string') {
465
477
  /* c8 ignore next */
478
+ /* v8 ignore next */
479
+ // Cooked text is always available for valid templates; fall back shields invalid escape sequences.
466
480
  chunk = quasi.value.raw ?? '';
467
481
  }
468
482
  if (trimStartNext > 0) {
@@ -478,6 +492,8 @@ const buildTemplateSource = (quasis, expressions, source, tag) => {
478
492
  const end = expression.end ?? null;
479
493
  if (start === null || end === null) {
480
494
  /* c8 ignore next */
495
+ /* v8 ignore next */
496
+ // Expressions parsed from tagged templates always include start/end ranges.
481
497
  throw new Error('Unable to read template expression source range.');
482
498
  }
483
499
  const nextChunk = quasis[index + 1];
@@ -548,6 +564,8 @@ const isLoaderPlaceholderIdentifier = (node) => {
548
564
  (node.type !== 'Identifier' && node.type !== 'JSXIdentifier') ||
549
565
  typeof node.name !== 'string') {
550
566
  /* c8 ignore next */
567
+ /* v8 ignore next */
568
+ // Visitor only calls this helper with identifier-like nodes; guard prevents crashes on malformed ASTs.
551
569
  return false;
552
570
  }
553
571
  return (node.name.startsWith(TEMPLATE_EXPR_PLACEHOLDER_PREFIX) ||
@@ -599,6 +617,8 @@ const transformSource = (source, config) => {
599
617
  return;
600
618
  }
601
619
  /* c8 ignore next */
620
+ /* v8 ignore next */
621
+ // Modes are validated during option parsing; this fallback guards future extensions.
602
622
  throw new Error(`[jsx-loader] Transformation mode "${mode}" not implemented yet for tag "${tagName}".`);
603
623
  });
604
624
  return {
@@ -1 +1,2 @@
1
- export declare const ensureNodeDom: () => Promise<void>;
1
+ export declare const ensureNodeDom: () => void;
2
+ export declare const __setNodeRequireForTesting: (mockRequire: NodeJS.Require | null) => void;
@@ -1,3 +1,7 @@
1
+ import { createRequire } from 'node:module';
2
+ const nodeRequire = createRequire(import.meta.url);
3
+ let requireOverride = null;
4
+ const resolveRequire = () => requireOverride ?? nodeRequire;
1
5
  const DOM_TEMPLATE = '<!doctype html><html><body></body></html>';
2
6
  const GLOBAL_KEYS = [
3
7
  'window',
@@ -23,13 +27,13 @@ const assignGlobalTargets = (windowObj) => {
23
27
  }
24
28
  });
25
29
  };
26
- const loadLinkedom = async () => {
27
- const { parseHTML } = await import('linkedom');
30
+ const loadLinkedom = () => {
31
+ const { parseHTML } = resolveRequire()('linkedom');
28
32
  const { window } = parseHTML(DOM_TEMPLATE);
29
33
  return window;
30
34
  };
31
- const loadJsdom = async () => {
32
- const { JSDOM } = await import('jsdom');
35
+ const loadJsdom = () => {
36
+ const { JSDOM } = resolveRequire()('jsdom');
33
37
  const { window } = new JSDOM(DOM_TEMPLATE);
34
38
  return window;
35
39
  };
@@ -52,11 +56,11 @@ const selectLoaders = () => {
52
56
  }
53
57
  return [loadLinkedom, loadJsdom];
54
58
  };
55
- const createShimWindow = async () => {
59
+ const createShimWindow = () => {
56
60
  const errors = [];
57
61
  for (const loader of selectLoaders()) {
58
62
  try {
59
- return await loader();
63
+ return loader();
60
64
  }
61
65
  catch (error) {
62
66
  errors.push(error);
@@ -65,19 +69,15 @@ const createShimWindow = async () => {
65
69
  const help = 'Unable to bootstrap a DOM-like environment. Install "linkedom" or "jsdom" (both optional peer dependencies) or set KNIGHTED_JSX_NODE_SHIM to pick one explicitly.';
66
70
  throw new AggregateError(errors, help);
67
71
  };
68
- let bootstrapPromise = null;
69
- export const ensureNodeDom = async () => {
70
- if (hasDom()) {
72
+ let bootstrapped = false;
73
+ export const ensureNodeDom = () => {
74
+ if (hasDom() || bootstrapped) {
71
75
  return;
72
76
  }
73
- if (!bootstrapPromise) {
74
- bootstrapPromise = (async () => {
75
- const windowObj = await createShimWindow();
76
- assignGlobalTargets(windowObj);
77
- })().catch(error => {
78
- bootstrapPromise = null;
79
- throw error;
80
- });
81
- }
82
- return bootstrapPromise;
77
+ const windowObj = createShimWindow();
78
+ assignGlobalTargets(windowObj);
79
+ bootstrapped = true;
80
+ };
81
+ export const __setNodeRequireForTesting = (mockRequire) => {
82
+ requireOverride = mockRequire;
83
83
  };
@@ -1,4 +1,4 @@
1
1
  import { ensureNodeDom } from './bootstrap.js';
2
2
  import { jsx as baseJsx } from '../jsx.js';
3
- await ensureNodeDom();
3
+ ensureNodeDom();
4
4
  export const jsx = baseJsx;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/jsx",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "description": "Runtime JSX tagged template that renders DOM or React trees anywhere without a build step.",
5
5
  "keywords": [
6
6
  "jsx runtime",
@@ -162,7 +162,7 @@
162
162
  "optionalDependencies": {
163
163
  "@oxc-parser/binding-darwin-arm64": "^0.105.0",
164
164
  "@oxc-parser/binding-linux-x64-gnu": "^0.105.0",
165
- "@oxc-parser/binding-wasm32-wasi": "0.105.0"
165
+ "@oxc-parser/binding-wasm32-wasi": "^0.105.0"
166
166
  },
167
167
  "overrides": {
168
168
  "module-lookup-amd": {