@knighted/jsx 1.2.0-rc.4 → 1.2.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.
- package/README.md +3 -1
- package/dist/cjs/jsx.cjs +4 -4
- package/dist/cjs/loader/jsx.cjs +52 -7
- package/dist/cjs/react/react-jsx.cjs +2 -4
- package/dist/cjs/runtime/shared.cjs +44 -7
- package/dist/cjs/runtime/shared.d.cts +3 -1
- package/dist/jsx.js +5 -5
- package/dist/lite/index.js +3 -3
- package/dist/lite/node/index.js +3 -3
- package/dist/lite/node/react/index.js +2 -2
- package/dist/lite/react/index.js +2 -2
- package/dist/loader/jsx.js +52 -7
- package/dist/react/react-jsx.js +3 -5
- package/dist/runtime/shared.d.ts +3 -1
- package/dist/runtime/shared.js +41 -4
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -136,6 +136,8 @@ npx http-server test/fixtures/rspack-app -p 4173
|
|
|
136
136
|
|
|
137
137
|
Visit `http://localhost:4173` (or whichever port you pick) to interact with both the Lit + React hybrid demo and the React-mode bundle.
|
|
138
138
|
|
|
139
|
+
Need a deeper dive into loader behavior and options? Check out [`src/loader/README.md`](src/loader/README.md) for a full walkthrough.
|
|
140
|
+
|
|
139
141
|
## Node / SSR usage
|
|
140
142
|
|
|
141
143
|
Import the dedicated Node entry (`@knighted/jsx/node`) when you want to run the template tag inside bare Node.js. It automatically bootstraps a DOM shim by loading either `linkedom` or `jsdom` (install one of them to opt in) and then re-exports the usual helpers so you can keep authoring JSX in the same way:
|
|
@@ -250,7 +252,7 @@ Build the fixture locally with `npx next build test/fixtures/next-app` (or run `
|
|
|
250
252
|
|
|
251
253
|
### Interpolations
|
|
252
254
|
|
|
253
|
-
- All dynamic values are provided through standard template literal expressions (`${...}`) and map to JSX exactly where they appear.
|
|
255
|
+
- All dynamic values are provided through standard template literal expressions (`${...}`) and map to JSX exactly where they appear. Interpolations used as text children no longer need an extra `{...}` wrapper—the runtime automatically recognizes placeholders inside text segments (so `Count is ${value}` just works). Use the usual JSX braces when the syntax requires them (`className={${value}}`, `{...props}`, conditionals, etc.).
|
|
254
256
|
- Every expression can be any JavaScript value: primitives, arrays/iterables, DOM nodes, functions, other `jsx` results, or custom component references.
|
|
255
257
|
- Async values (Promises) are not supported. Resolve them before passing into the template.
|
|
256
258
|
|
package/dist/cjs/jsx.cjs
CHANGED
|
@@ -162,10 +162,10 @@ const evaluateJsxChildren = (children, ctx, namespace) => {
|
|
|
162
162
|
children.forEach(child => {
|
|
163
163
|
switch (child.type) {
|
|
164
164
|
case 'JSXText': {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
resolved.push(
|
|
168
|
-
}
|
|
165
|
+
const segments = (0, shared_js_1.normalizeJsxTextSegments)(child.value, ctx.placeholders);
|
|
166
|
+
segments.forEach(segment => {
|
|
167
|
+
resolved.push(segment);
|
|
168
|
+
});
|
|
169
169
|
break;
|
|
170
170
|
}
|
|
171
171
|
case 'JSXExpressionContainer': {
|
package/dist/cjs/loader/jsx.cjs
CHANGED
|
@@ -31,10 +31,14 @@ class ReactTemplateBuilder {
|
|
|
31
31
|
children.forEach(child => {
|
|
32
32
|
switch (child.type) {
|
|
33
33
|
case 'JSXText': {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const segments = normalizeJsxTextSegments(child.value, this.placeholderMap);
|
|
35
|
+
segments.forEach(segment => {
|
|
36
|
+
if (segment.kind === 'text') {
|
|
37
|
+
compiled.push(JSON.stringify(segment.value));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
compiled.push(segment.value);
|
|
41
|
+
});
|
|
38
42
|
break;
|
|
39
43
|
}
|
|
40
44
|
case 'JSXExpressionContainer': {
|
|
@@ -154,6 +158,9 @@ class ReactTemplateBuilder {
|
|
|
154
158
|
}
|
|
155
159
|
return node.name;
|
|
156
160
|
}
|
|
161
|
+
if (node.type === 'Literal') {
|
|
162
|
+
return JSON.stringify(node.value);
|
|
163
|
+
}
|
|
157
164
|
if ('range' in node && Array.isArray(node.range)) {
|
|
158
165
|
throw new Error('[jsx-loader] Unable to inline complex expressions in react mode.');
|
|
159
166
|
}
|
|
@@ -198,6 +205,7 @@ const getTemplateExpressionContext = (left, right) => {
|
|
|
198
205
|
return { type: 'childText' };
|
|
199
206
|
};
|
|
200
207
|
const TEMPLATE_EXPR_PLACEHOLDER_PREFIX = '__JSX_LOADER_TEMPLATE_EXPR_';
|
|
208
|
+
const TEMPLATE_EXPR_PLACEHOLDER_PATTERN = new RegExp(`${TEMPLATE_EXPR_PLACEHOLDER_PREFIX}\\d+__`, 'g');
|
|
201
209
|
const MODULE_PARSER_OPTIONS = {
|
|
202
210
|
lang: 'tsx',
|
|
203
211
|
sourceType: 'module',
|
|
@@ -392,10 +400,47 @@ const extractJsxRoot = (program) => {
|
|
|
392
400
|
}
|
|
393
401
|
throw new Error('[jsx-loader] Expected the template to contain a single JSX root node.');
|
|
394
402
|
};
|
|
395
|
-
const
|
|
403
|
+
const normalizeJsxTextSegments = (value, placeholders) => {
|
|
396
404
|
const collapsed = value.replace(/\r/g, '').replace(/\n\s+/g, ' ');
|
|
397
|
-
const
|
|
398
|
-
|
|
405
|
+
const leadingWhitespace = value.match(/^\s*/)?.[0] ?? '';
|
|
406
|
+
const trailingWhitespace = value.match(/\s*$/)?.[0] ?? '';
|
|
407
|
+
const trimStart = /\n/.test(leadingWhitespace);
|
|
408
|
+
const trimEnd = /\n/.test(trailingWhitespace);
|
|
409
|
+
let normalized = collapsed;
|
|
410
|
+
if (trimStart) {
|
|
411
|
+
normalized = normalized.replace(/^\s+/, '');
|
|
412
|
+
}
|
|
413
|
+
if (trimEnd) {
|
|
414
|
+
normalized = normalized.replace(/\s+$/, '');
|
|
415
|
+
}
|
|
416
|
+
if (normalized.length === 0 || normalized.trim().length === 0) {
|
|
417
|
+
return [];
|
|
418
|
+
}
|
|
419
|
+
const segments = [];
|
|
420
|
+
TEMPLATE_EXPR_PLACEHOLDER_PATTERN.lastIndex = 0;
|
|
421
|
+
let cursor = 0;
|
|
422
|
+
let match;
|
|
423
|
+
while ((match = TEMPLATE_EXPR_PLACEHOLDER_PATTERN.exec(normalized))) {
|
|
424
|
+
const index = match.index;
|
|
425
|
+
const slice = normalized.slice(cursor, index);
|
|
426
|
+
if (slice) {
|
|
427
|
+
segments.push({ kind: 'text', value: slice });
|
|
428
|
+
}
|
|
429
|
+
const marker = match[0];
|
|
430
|
+
const expression = placeholders.get(marker);
|
|
431
|
+
if (expression) {
|
|
432
|
+
segments.push({ kind: 'expression', value: expression });
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
segments.push({ kind: 'text', value: marker });
|
|
436
|
+
}
|
|
437
|
+
cursor = index + marker.length;
|
|
438
|
+
}
|
|
439
|
+
const remainder = normalized.slice(cursor);
|
|
440
|
+
if (remainder) {
|
|
441
|
+
segments.push({ kind: 'text', value: remainder });
|
|
442
|
+
}
|
|
443
|
+
return segments;
|
|
399
444
|
};
|
|
400
445
|
const TAG_PLACEHOLDER_PREFIX = '__JSX_LOADER_TAG_EXPR_';
|
|
401
446
|
const buildTemplateSource = (quasis, expressions, source, tag) => {
|
|
@@ -72,10 +72,8 @@ const evaluateReactJsxChildren = (children, ctx) => {
|
|
|
72
72
|
children.forEach(child => {
|
|
73
73
|
switch (child.type) {
|
|
74
74
|
case 'JSXText': {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
resolved.push(text);
|
|
78
|
-
}
|
|
75
|
+
const segments = (0, shared_js_1.normalizeJsxTextSegments)(child.value, ctx.placeholders);
|
|
76
|
+
segments.forEach(segment => appendReactChild(resolved, segment));
|
|
79
77
|
break;
|
|
80
78
|
}
|
|
81
79
|
case 'JSXExpressionContainer': {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildTemplate = exports.ensureBinding = exports.sanitizeIdentifier = exports.evaluateExpression = exports.collectPlaceholderNames = exports.
|
|
3
|
+
exports.buildTemplate = exports.ensureBinding = exports.sanitizeIdentifier = exports.evaluateExpression = exports.collectPlaceholderNames = exports.normalizeJsxTextSegments = exports.walkAst = exports.getIdentifierName = exports.extractRootNode = exports.formatParserError = exports.parserOptions = exports.placeholderPattern = exports.PLACEHOLDER_PREFIX = void 0;
|
|
4
4
|
const OPEN_TAG_RE = /<\s*$/;
|
|
5
5
|
const CLOSE_TAG_RE = /<\/\s*$/;
|
|
6
|
-
|
|
6
|
+
exports.PLACEHOLDER_PREFIX = '__KX_EXPR__';
|
|
7
|
+
exports.placeholderPattern = new RegExp(`${exports.PLACEHOLDER_PREFIX}\\d+_\\d+__`, 'g');
|
|
7
8
|
let invocationCounter = 0;
|
|
8
9
|
exports.parserOptions = {
|
|
9
10
|
lang: 'jsx',
|
|
@@ -73,12 +74,48 @@ const walkAst = (node, visitor) => {
|
|
|
73
74
|
});
|
|
74
75
|
};
|
|
75
76
|
exports.walkAst = walkAst;
|
|
76
|
-
const
|
|
77
|
+
const normalizeJsxTextSegments = (value, placeholders) => {
|
|
77
78
|
const collapsed = value.replace(/\r/g, '').replace(/\n\s+/g, ' ');
|
|
78
|
-
const
|
|
79
|
-
|
|
79
|
+
const leadingWhitespace = value.match(/^\s*/)?.[0] ?? '';
|
|
80
|
+
const trailingWhitespace = value.match(/\s*$/)?.[0] ?? '';
|
|
81
|
+
const trimStart = /\n/.test(leadingWhitespace);
|
|
82
|
+
const trimEnd = /\n/.test(trailingWhitespace);
|
|
83
|
+
let normalized = collapsed;
|
|
84
|
+
if (trimStart) {
|
|
85
|
+
normalized = normalized.replace(/^\s+/, '');
|
|
86
|
+
}
|
|
87
|
+
if (trimEnd) {
|
|
88
|
+
normalized = normalized.replace(/\s+$/, '');
|
|
89
|
+
}
|
|
90
|
+
if (normalized.length === 0 || normalized.trim().length === 0) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const segments = [];
|
|
94
|
+
exports.placeholderPattern.lastIndex = 0;
|
|
95
|
+
let cursor = 0;
|
|
96
|
+
let match;
|
|
97
|
+
while ((match = exports.placeholderPattern.exec(normalized))) {
|
|
98
|
+
const index = match.index;
|
|
99
|
+
const slice = normalized.slice(cursor, index);
|
|
100
|
+
if (slice) {
|
|
101
|
+
segments.push(slice);
|
|
102
|
+
}
|
|
103
|
+
const token = match[0];
|
|
104
|
+
if (placeholders.has(token)) {
|
|
105
|
+
segments.push(placeholders.get(token));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
segments.push(token);
|
|
109
|
+
}
|
|
110
|
+
cursor = index + token.length;
|
|
111
|
+
}
|
|
112
|
+
const remainder = normalized.slice(cursor);
|
|
113
|
+
if (remainder) {
|
|
114
|
+
segments.push(remainder);
|
|
115
|
+
}
|
|
116
|
+
return segments;
|
|
80
117
|
};
|
|
81
|
-
exports.
|
|
118
|
+
exports.normalizeJsxTextSegments = normalizeJsxTextSegments;
|
|
82
119
|
const collectPlaceholderNames = (expression, ctx) => {
|
|
83
120
|
const placeholders = new Set();
|
|
84
121
|
(0, exports.walkAst)(expression, node => {
|
|
@@ -160,7 +197,7 @@ const buildTemplate = (strings, values) => {
|
|
|
160
197
|
source += value + nextChunk;
|
|
161
198
|
continue;
|
|
162
199
|
}
|
|
163
|
-
const placeholder = `${PLACEHOLDER_PREFIX}${templateId}_${placeholderIndex++}__`;
|
|
200
|
+
const placeholder = `${exports.PLACEHOLDER_PREFIX}${templateId}_${placeholderIndex++}__`;
|
|
164
201
|
placeholders.set(placeholder, value);
|
|
165
202
|
source += placeholder + nextChunk;
|
|
166
203
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Expression, JSXElement, JSXFragment, JSXIdentifier, JSXMemberExpression, JSXNamespacedName, Program } from '@oxc-project/types';
|
|
2
2
|
import type { OxcError, ParserOptions } from 'oxc-parser';
|
|
3
|
+
export declare const PLACEHOLDER_PREFIX = "__KX_EXPR__";
|
|
4
|
+
export declare const placeholderPattern: RegExp;
|
|
3
5
|
type AnyTemplateFunction = (...args: any[]) => unknown;
|
|
4
6
|
type AnyTemplateConstructor = abstract new (...args: any[]) => unknown;
|
|
5
7
|
export type TemplateComponent = (AnyTemplateFunction | AnyTemplateConstructor) & {
|
|
@@ -29,7 +31,7 @@ type AnyOxcNode = {
|
|
|
29
31
|
[key: string]: unknown;
|
|
30
32
|
};
|
|
31
33
|
export declare const walkAst: (node: unknown, visitor: (target: AnyOxcNode) => void) => void;
|
|
32
|
-
export declare const
|
|
34
|
+
export declare const normalizeJsxTextSegments: (value: string, placeholders: Map<string, unknown>) => unknown[];
|
|
33
35
|
export declare const collectPlaceholderNames: <TComponent extends TemplateComponent>(expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>) => string[];
|
|
34
36
|
export declare const evaluateExpression: <TComponent extends TemplateComponent>(expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>, evaluateJsxNode: (node: JSXElement | JSXFragment) => unknown) => unknown;
|
|
35
37
|
export declare const sanitizeIdentifier: (value: string) => string;
|
package/dist/jsx.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseSync } from 'oxc-parser';
|
|
2
|
-
import { buildTemplate, evaluateExpression, extractRootNode, formatParserError, getIdentifierName,
|
|
2
|
+
import { buildTemplate, evaluateExpression, extractRootNode, formatParserError, getIdentifierName, normalizeJsxTextSegments, parserOptions, } from './runtime/shared.js';
|
|
3
3
|
const ensureDomAvailable = () => {
|
|
4
4
|
if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
|
|
5
5
|
throw new Error('The jsx template tag requires a DOM-like environment (document missing).');
|
|
@@ -159,10 +159,10 @@ const evaluateJsxChildren = (children, ctx, namespace) => {
|
|
|
159
159
|
children.forEach(child => {
|
|
160
160
|
switch (child.type) {
|
|
161
161
|
case 'JSXText': {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
resolved.push(
|
|
165
|
-
}
|
|
162
|
+
const segments = normalizeJsxTextSegments(child.value, ctx.placeholders);
|
|
163
|
+
segments.forEach(segment => {
|
|
164
|
+
resolved.push(segment);
|
|
165
|
+
});
|
|
166
166
|
break;
|
|
167
167
|
}
|
|
168
168
|
case 'JSXExpressionContainer': {
|
package/dist/lite/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {parseSync}from'oxc-parser';var
|
|
1
|
+
import {parseSync}from'oxc-parser';var $=/<\s*$/,_=/<\/\s*$/,C="__KX_EXPR__",S=new RegExp(`${C}\\d+_\\d+__`,"g"),F=0,b={lang:"jsx",sourceType:"module",range:true,preserveParens:true},w=e=>{let n=`[oxc-parser] ${e.message}`;if(e.labels?.length){let t=e.labels[0];t.message&&(n+=`
|
|
2
2
|
${t.message}`);}return e.codeframe&&(n+=`
|
|
3
|
-
${e.codeframe}`),n},
|
|
4
|
-
export{
|
|
3
|
+
${e.codeframe}`),n},T=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.")},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,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=>h(o,n));return}typeof r=="object"&&h(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]??"",a=/\n/.test(r),s=/\n/.test(o),i=t;if(a&&(i=i.replace(/^\s+/,"")),s&&(i=i.replace(/\s+$/,"")),i.length===0||i.trim().length===0)return [];let p=[];S.lastIndex=0;let c=0,l;for(;l=S.exec(i);){let m=l.index,f=i.slice(c,m);f&&p.push(f);let u=l[0];n.has(u)?p.push(n.get(u)):p.push(u),c=m+u.length;}let d=i.slice(c);return d&&p.push(d),p},P=(e,n)=>{let t=new Set;return h(e,r=>{r.type==="Identifier"&&n.placeholders.has(r.name)&&t.add(r.name);}),Array.from(t)},N=(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,a=n.source.slice(r,o),s=P(e,n);try{let i=new Function(...s,`"use strict"; return (${a});`),p=s.map(c=>n.placeholders.get(c));return i(...p)}catch(i){throw new Error(`Failed to evaluate expression ${a}: ${i.message}`)}},I=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},O=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,a=I(o??""),s=a,i=1;for(;n.some(c=>c.name===s);)s=`${a}${i++}`;let p={name:s,value:e};return n.push(p),t.set(e,p),p},k=(e,n)=>{let t=e.raw??e,r=new Map,o=[],a=new Map,s=t[0]??"",i=F++,p=0;for(let c=0;c<n.length;c++){let l=t[c]??"",d=t[c+1]??"",m=n[c],f=$.test(l)||_.test(l);if(f&&typeof m=="function"){let j=O(m,o,a);s+=j.name+d;continue}if(f&&typeof m=="string"){s+=m+d;continue}let u=`${C}${i}_${p++}__`;r.set(u,m),s+=u+d;}return {source:s,placeholders:r,bindings:o}};var D=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},L=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,B=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",A=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",W=(e,n,t)=>{if(!(t===false||t===null||t===void 0)){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 r=t,o=e.style;if(!o)return;let a=o;Object.entries(r).forEach(([s,i])=>{if(i!=null){if(s.startsWith("--")){o.setProperty(s,String(i));return}a[s]=i;}});return}if(typeof t=="function"&&n.startsWith("on")){let r=n.slice(2).toLowerCase();e.addEventListener(r,t);return}if(n==="class"||n==="className"){let r=Array.isArray(t)?t.filter(Boolean).join(" "):String(t);e.setAttribute("class",r);return}if(n==="htmlFor"){e.setAttribute("for",String(t));return}if(n in e&&!n.includes("-")){e[n]=t;return}e.setAttribute(n,t===true?"":String(t));}},g=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(A(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(B(n)){for(let t of n)g(e,t);return}if(L(n)){e.appendChild(n);return}e.appendChild(document.createTextNode(String(n)));}},y=(e,n,t)=>N(e,n,r=>J(r,n,t)),R=(e,n,t)=>{let r={};return e.forEach(o=>{if(o.type==="JSXSpreadAttribute"){let s=y(o.argument,n,t);s&&typeof s=="object"&&!Array.isArray(s)&&Object.assign(r,s);return}let a=x(o.name);if(!o.value){r[a]=true;return}if(o.value.type==="Literal"){r[a]=o.value.value;return}if(o.value.type==="JSXExpressionContainer"){if(o.value.expression.type==="JSXEmptyExpression")return;r[a]=y(o.value.expression,n,t);}}),r},z=(e,n,t,r)=>{let o=R(n,t,r);Object.entries(o).forEach(([a,s])=>{if(a!=="key"){if(a==="children"){g(e,s);return}W(e,a,s);}});},E=(e,n,t)=>{let r=[];return e.forEach(o=>{switch(o.type){case "JSXText":{X(o.value,n.placeholders).forEach(s=>{r.push(s);});break}case "JSXExpressionContainer":{if(o.expression.type==="JSXEmptyExpression")break;r.push(y(o.expression,n,t));break}case "JSXSpreadChild":{let a=y(o.expression,n,t);a!=null&&r.push(a);break}case "JSXElement":case "JSXFragment":{r.push(J(o,n,t));break}}}),r},V=(e,n,t,r)=>{let o=R(e.openingElement.attributes,n,r),a=E(e.children,n,r);a.length===1?o.children=a[0]:a.length>1&&(o.children=a);let s=t(o);if(A(s))throw new Error("Async jsx components are not supported.");return s},H=(e,n,t)=>{let r=e.openingElement,o=x(r.name),a=n.components.get(o);if(a)return V(e,n,a,t);if(/[A-Z]/.test(o[0]??""))throw new Error(`Unknown component "${o}". Did you interpolate it with the template literal?`);let s=o==="svg"?"svg":t,i=o==="foreignObject"?null:s,p=s==="svg"?document.createElementNS("http://www.w3.org/2000/svg",o):document.createElement(o);return z(p,r.attributes,n,s),E(e.children,n,i).forEach(l=>g(p,l)),p},J=(e,n,t)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return E(e.children,n,t).forEach(a=>g(r,a)),r}return H(e,n,t)},Z=(e,...n)=>{D();let t=k(e,n),r=parseSync("inline.jsx",t.source,b);if(r.errors.length>0)throw new Error(w(r.errors[0]));let o=T(r.program),a={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(s=>[s.name,s.value]))};return J(o,a,null)};
|
|
4
|
+
export{Z as jsx};
|
package/dist/lite/node/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {parseSync}from'oxc-parser';var C="<!doctype html><html><body></body></html>"
|
|
1
|
+
import {parseSync}from'oxc-parser';var C="<!doctype html><html><body></body></html>",$=["window","self","document","HTMLElement","Element","Node","DocumentFragment","customElements","Text","Comment","MutationObserver","navigator"],L=()=>typeof document<"u"&&typeof document.createElement=="function",F=e=>{let n=globalThis,t=e;$.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(C);return n},S=async()=>{let{JSDOM:e}=await import('jsdom'),{window:n}=new e(C);return n},W=()=>{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"},B=()=>{let e=W();return e==="linkedom"?[E,S]:e==="jsdom"?[S,E]:[E,S]},H=async()=>{let e=[];for(let t of B())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)},x=null,T=async()=>{if(!L())return x||(x=(async()=>{let e=await H();F(e);})().catch(e=>{throw x=null,e})),x};var G=/<\s*$/,z=/<\/\s*$/,X="__KX_EXPR__",N=new RegExp(`${X}\\d+_\\d+__`,"g"),K=0,k={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
2
|
${t.message}`);}return e.codeframe&&(n+=`
|
|
3
|
-
${e.codeframe}`),n},
|
|
4
|
-
export{
|
|
3
|
+
${e.codeframe}`),n},R=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 ""}},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);}}));},_=(e,n)=>{let t=e.replace(/\r/g,"").replace(/\n\s+/g," "),r=e.match(/^\s*/)?.[0]??"",o=e.match(/\s*$/)?.[0]??"",a=/\n/.test(r),s=/\n/.test(o),i=t;if(a&&(i=i.replace(/^\s+/,"")),s&&(i=i.replace(/\s+$/,"")),i.length===0||i.trim().length===0)return [];let c=[];N.lastIndex=0;let p=0,m;for(;m=N.exec(i);){let l=m.index,f=i.slice(p,l);f&&c.push(f);let d=m[0];n.has(d)?c.push(n.get(d)):c.push(d),p=l+d.length;}let u=i.slice(p);return u&&c.push(u),c},V=(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)},j=(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,a=n.source.slice(r,o),s=V(e,n);try{let i=new Function(...s,`"use strict"; return (${a});`),c=s.map(p=>n.placeholders.get(p));return i(...c)}catch(i){throw new Error(`Failed to evaluate expression ${a}: ${i.message}`)}},U=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},Z=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,a=U(o??""),s=a,i=1;for(;n.some(p=>p.name===s);)s=`${a}${i++}`;let c={name:s,value:e};return n.push(c),t.set(e,c),c},P=(e,n)=>{let t=e.raw??e,r=new Map,o=[],a=new Map,s=t[0]??"",i=K++,c=0;for(let p=0;p<n.length;p++){let m=t[p]??"",u=t[p+1]??"",l=n[p],f=G.test(m)||z.test(m);if(f&&typeof l=="function"){let D=Z(l,o,a);s+=D.name+u;continue}if(f&&typeof l=="string"){s+=l+u;continue}let d=`${X}${i}_${c++}__`;r.set(d,l),s+=d+u;}return {source:s,placeholders:r,bindings:o}};var Y=()=>{if(typeof document>"u"||typeof document.createElement!="function")throw new Error("The jsx template tag requires a DOM-like environment (document missing).")},Q=e=>typeof Node>"u"?false:e instanceof Node||e instanceof DocumentFragment,v=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",O=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",ee=(e,n,t)=>{if(!(t===false||t===null||t===void 0)){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 r=t,o=e.style;if(!o)return;let a=o;Object.entries(r).forEach(([s,i])=>{if(i!=null){if(s.startsWith("--")){o.setProperty(s,String(i));return}a[s]=i;}});return}if(typeof t=="function"&&n.startsWith("on")){let r=n.slice(2).toLowerCase();e.addEventListener(r,t);return}if(n==="class"||n==="className"){let r=Array.isArray(t)?t.filter(Boolean).join(" "):String(t);e.setAttribute("class",r);return}if(n==="htmlFor"){e.setAttribute("for",String(t));return}if(n in e&&!n.includes("-")){e[n]=t;return}e.setAttribute(n,t===true?"":String(t));}},g=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(O(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(v(n)){for(let t of n)g(e,t);return}if(Q(n)){e.appendChild(n);return}e.appendChild(document.createTextNode(String(n)));}},h=(e,n,t)=>j(e,n,r=>b(r,n,t)),I=(e,n,t)=>{let r={};return e.forEach(o=>{if(o.type==="JSXSpreadAttribute"){let s=h(o.argument,n,t);s&&typeof s=="object"&&!Array.isArray(s)&&Object.assign(r,s);return}let a=y(o.name);if(!o.value){r[a]=true;return}if(o.value.type==="Literal"){r[a]=o.value.value;return}if(o.value.type==="JSXExpressionContainer"){if(o.value.expression.type==="JSXEmptyExpression")return;r[a]=h(o.value.expression,n,t);}}),r},ne=(e,n,t,r)=>{let o=I(n,t,r);Object.entries(o).forEach(([a,s])=>{if(a!=="key"){if(a==="children"){g(e,s);return}ee(e,a,s);}});},J=(e,n,t)=>{let r=[];return e.forEach(o=>{switch(o.type){case "JSXText":{_(o.value,n.placeholders).forEach(s=>{r.push(s);});break}case "JSXExpressionContainer":{if(o.expression.type==="JSXEmptyExpression")break;r.push(h(o.expression,n,t));break}case "JSXSpreadChild":{let a=h(o.expression,n,t);a!=null&&r.push(a);break}case "JSXElement":case "JSXFragment":{r.push(b(o,n,t));break}}}),r},te=(e,n,t,r)=>{let o=I(e.openingElement.attributes,n,r),a=J(e.children,n,r);a.length===1?o.children=a[0]:a.length>1&&(o.children=a);let s=t(o);if(O(s))throw new Error("Async jsx components are not supported.");return s},re=(e,n,t)=>{let r=e.openingElement,o=y(r.name),a=n.components.get(o);if(a)return te(e,n,a,t);if(/[A-Z]/.test(o[0]??""))throw new Error(`Unknown component "${o}". Did you interpolate it with the template literal?`);let s=o==="svg"?"svg":t,i=o==="foreignObject"?null:s,c=s==="svg"?document.createElementNS("http://www.w3.org/2000/svg",o):document.createElement(o);return ne(c,r.attributes,n,s),J(e.children,n,i).forEach(m=>g(c,m)),c},b=(e,n,t)=>{if(e.type==="JSXFragment"){let r=document.createDocumentFragment();return J(e.children,n,t).forEach(a=>g(r,a)),r}return re(e,n,t)},M=(e,...n)=>{Y();let t=P(e,n),r=parseSync("inline.jsx",t.source,k);if(r.errors.length>0)throw new Error(A(r.errors[0]));let o=R(r.program),a={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(s=>[s.name,s.value]))};return b(o,a,null)};await T();var le=M;
|
|
4
|
+
export{le as jsx};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {parseSync}from'oxc-parser';import {createElement,Fragment}from'react';var
|
|
1
|
+
import {parseSync}from'oxc-parser';import {createElement,Fragment}from'react';var F=/<\s*$/,P=/<\/\s*$/,h="__KX_EXPR__",C=new RegExp(`${h}\\d+_\\d+__`,"g"),_=0,S={lang:"jsx",sourceType:"module",range:true,preserveParens:true},T=e=>{let n=`[oxc-parser] ${e.message}`;if(e.labels?.length){let t=e.labels[0];t.message&&(n+=`
|
|
2
2
|
${t.message}`);}return e.codeframe&&(n+=`
|
|
3
|
-
${e.codeframe}`),n},
|
|
3
|
+
${e.codeframe}`),n},w=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.")},f=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${f(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),p=t;if(s&&(p=p.replace(/^\s+/,"")),a&&(p=p.replace(/\s+$/,"")),p.length===0||p.trim().length===0)return [];let c=[];C.lastIndex=0;let i=0,u;for(;u=C.exec(p);){let m=u.index,d=p.slice(i,m);d&&c.push(d);let l=u[0];n.has(l)?c.push(n.get(l)):c.push(l),i=m+l.length;}let g=p.slice(i);return g&&c.push(g),c},I=(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)},R=(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=I(e,n);try{let p=new Function(...a,`"use strict"; return (${s});`),c=a.map(i=>n.placeholders.get(i));return p(...c)}catch(p){throw new Error(`Failed to evaluate expression ${s}: ${p.message}`)}},O=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},j=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,s=O(o??""),a=s,p=1;for(;n.some(i=>i.name===a);)a=`${s}${p++}`;let c={name:a,value:e};return n.push(c),t.set(e,c),c},k=(e,n)=>{let t=e.raw??e,r=new Map,o=[],s=new Map,a=t[0]??"",p=_++,c=0;for(let i=0;i<n.length;i++){let u=t[i]??"",g=t[i+1]??"",m=n[i],d=F.test(u)||P.test(u);if(d&&typeof m=="function"){let $=j(m,o,s);a+=$.name+g;continue}if(d&&typeof m=="string"){a+=m+g;continue}let l=`${h}${p}_${c++}__`;r.set(l,m),a+=l+g;}return {source:a,placeholders:r,bindings:o}};var z=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",L=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",x=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(L(n))throw new Error("Async values are not supported inside reactJsx template results.");if(Array.isArray(n)){n.forEach(t=>x(e,t));return}if(z(n)){for(let t of n)x(e,t);return}e.push(n);}},y=(e,n)=>R(e,n,t=>J(t,n)),v=(e,n)=>{let t={};return e.forEach(r=>{if(r.type==="JSXSpreadAttribute"){let s=y(r.argument,n);s&&typeof s=="object"&&!Array.isArray(s)&&Object.assign(t,s);return}let o=f(r.name);if(!r.value){t[o]=true;return}if(r.value.type==="Literal"){t[o]=r.value.value;return}if(r.value.type==="JSXExpressionContainer"){if(r.value.expression.type==="JSXEmptyExpression")return;t[o]=y(r.value.expression,n);}}),t},N=(e,n)=>{let t=[];return e.forEach(r=>{switch(r.type){case "JSXText":{X(r.value,n.placeholders).forEach(s=>x(t,s));break}case "JSXExpressionContainer":{if(r.expression.type==="JSXEmptyExpression")break;x(t,y(r.expression,n));break}case "JSXSpreadChild":{let o=y(r.expression,n);o!=null&&x(t,o);break}case "JSXElement":case "JSXFragment":{t.push(J(r,n));break}}}),t},A=(e,n,t)=>createElement(e,n,...t),V=(e,n)=>{let t=e.openingElement,r=f(t.name),o=n.components.get(r),s=v(t.attributes,n),a=N(e.children,n);if(o)return A(o,s,a);if(/[A-Z]/.test(r[0]??""))throw new Error(`Unknown component "${r}". Did you interpolate it with the template literal?`);return A(r,s,a)},J=(e,n)=>{if(e.type==="JSXFragment"){let t=N(e.children,n);return createElement(Fragment,null,...t)}return V(e,n)},Z=(e,...n)=>{let t=k(e,n),r=parseSync("inline.jsx",t.source,S);if(r.errors.length>0)throw new Error(T(r.errors[0]));let o=w(r.program),s={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(a=>[a.name,a.value]))};return J(o,s)};export{Z as reactJsx};
|
package/dist/lite/react/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {parseSync}from'oxc-parser';import {createElement,Fragment}from'react';var
|
|
1
|
+
import {parseSync}from'oxc-parser';import {createElement,Fragment}from'react';var F=/<\s*$/,P=/<\/\s*$/,h="__KX_EXPR__",C=new RegExp(`${h}\\d+_\\d+__`,"g"),_=0,S={lang:"jsx",sourceType:"module",range:true,preserveParens:true},T=e=>{let n=`[oxc-parser] ${e.message}`;if(e.labels?.length){let t=e.labels[0];t.message&&(n+=`
|
|
2
2
|
${t.message}`);}return e.codeframe&&(n+=`
|
|
3
|
-
${e.codeframe}`),n},
|
|
3
|
+
${e.codeframe}`),n},w=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.")},f=e=>{switch(e.type){case "JSXIdentifier":return e.name;case "JSXNamespacedName":return `${e.namespace.name}:${e.name.name}`;case "JSXMemberExpression":return `${f(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),p=t;if(s&&(p=p.replace(/^\s+/,"")),a&&(p=p.replace(/\s+$/,"")),p.length===0||p.trim().length===0)return [];let c=[];C.lastIndex=0;let i=0,u;for(;u=C.exec(p);){let m=u.index,d=p.slice(i,m);d&&c.push(d);let l=u[0];n.has(l)?c.push(n.get(l)):c.push(l),i=m+l.length;}let g=p.slice(i);return g&&c.push(g),c},I=(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)},R=(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=I(e,n);try{let p=new Function(...a,`"use strict"; return (${s});`),c=a.map(i=>n.placeholders.get(i));return p(...c)}catch(p){throw new Error(`Failed to evaluate expression ${s}: ${p.message}`)}},O=e=>{let n=e.replace(/[^a-zA-Z0-9_$]/g,"");return n?/[A-Za-z_$]/.test(n[0])?n:`Component${n}`:"Component"},j=(e,n,t)=>{let r=t.get(e);if(r)return r;let o=e.displayName||e.name||`Component${n.length}`,s=O(o??""),a=s,p=1;for(;n.some(i=>i.name===a);)a=`${s}${p++}`;let c={name:a,value:e};return n.push(c),t.set(e,c),c},k=(e,n)=>{let t=e.raw??e,r=new Map,o=[],s=new Map,a=t[0]??"",p=_++,c=0;for(let i=0;i<n.length;i++){let u=t[i]??"",g=t[i+1]??"",m=n[i],d=F.test(u)||P.test(u);if(d&&typeof m=="function"){let $=j(m,o,s);a+=$.name+g;continue}if(d&&typeof m=="string"){a+=m+g;continue}let l=`${h}${p}_${c++}__`;r.set(l,m),a+=l+g;}return {source:a,placeholders:r,bindings:o}};var z=e=>!e||typeof e=="string"?false:typeof e[Symbol.iterator]=="function",L=e=>!e||typeof e!="object"&&typeof e!="function"?false:typeof e.then=="function",x=(e,n)=>{if(n!=null&&typeof n!="boolean"){if(L(n))throw new Error("Async values are not supported inside reactJsx template results.");if(Array.isArray(n)){n.forEach(t=>x(e,t));return}if(z(n)){for(let t of n)x(e,t);return}e.push(n);}},y=(e,n)=>R(e,n,t=>J(t,n)),v=(e,n)=>{let t={};return e.forEach(r=>{if(r.type==="JSXSpreadAttribute"){let s=y(r.argument,n);s&&typeof s=="object"&&!Array.isArray(s)&&Object.assign(t,s);return}let o=f(r.name);if(!r.value){t[o]=true;return}if(r.value.type==="Literal"){t[o]=r.value.value;return}if(r.value.type==="JSXExpressionContainer"){if(r.value.expression.type==="JSXEmptyExpression")return;t[o]=y(r.value.expression,n);}}),t},N=(e,n)=>{let t=[];return e.forEach(r=>{switch(r.type){case "JSXText":{X(r.value,n.placeholders).forEach(s=>x(t,s));break}case "JSXExpressionContainer":{if(r.expression.type==="JSXEmptyExpression")break;x(t,y(r.expression,n));break}case "JSXSpreadChild":{let o=y(r.expression,n);o!=null&&x(t,o);break}case "JSXElement":case "JSXFragment":{t.push(J(r,n));break}}}),t},A=(e,n,t)=>createElement(e,n,...t),V=(e,n)=>{let t=e.openingElement,r=f(t.name),o=n.components.get(r),s=v(t.attributes,n),a=N(e.children,n);if(o)return A(o,s,a);if(/[A-Z]/.test(r[0]??""))throw new Error(`Unknown component "${r}". Did you interpolate it with the template literal?`);return A(r,s,a)},J=(e,n)=>{if(e.type==="JSXFragment"){let t=N(e.children,n);return createElement(Fragment,null,...t)}return V(e,n)},Z=(e,...n)=>{let t=k(e,n),r=parseSync("inline.jsx",t.source,S);if(r.errors.length>0)throw new Error(T(r.errors[0]));let o=w(r.program),s={source:t.source,placeholders:t.placeholders,components:new Map(t.bindings.map(a=>[a.name,a.value]))};return J(o,s)};export{Z as reactJsx};
|
package/dist/loader/jsx.js
CHANGED
|
@@ -25,10 +25,14 @@ class ReactTemplateBuilder {
|
|
|
25
25
|
children.forEach(child => {
|
|
26
26
|
switch (child.type) {
|
|
27
27
|
case 'JSXText': {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const segments = normalizeJsxTextSegments(child.value, this.placeholderMap);
|
|
29
|
+
segments.forEach(segment => {
|
|
30
|
+
if (segment.kind === 'text') {
|
|
31
|
+
compiled.push(JSON.stringify(segment.value));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
compiled.push(segment.value);
|
|
35
|
+
});
|
|
32
36
|
break;
|
|
33
37
|
}
|
|
34
38
|
case 'JSXExpressionContainer': {
|
|
@@ -148,6 +152,9 @@ class ReactTemplateBuilder {
|
|
|
148
152
|
}
|
|
149
153
|
return node.name;
|
|
150
154
|
}
|
|
155
|
+
if (node.type === 'Literal') {
|
|
156
|
+
return JSON.stringify(node.value);
|
|
157
|
+
}
|
|
151
158
|
if ('range' in node && Array.isArray(node.range)) {
|
|
152
159
|
throw new Error('[jsx-loader] Unable to inline complex expressions in react mode.');
|
|
153
160
|
}
|
|
@@ -192,6 +199,7 @@ const getTemplateExpressionContext = (left, right) => {
|
|
|
192
199
|
return { type: 'childText' };
|
|
193
200
|
};
|
|
194
201
|
const TEMPLATE_EXPR_PLACEHOLDER_PREFIX = '__JSX_LOADER_TEMPLATE_EXPR_';
|
|
202
|
+
const TEMPLATE_EXPR_PLACEHOLDER_PATTERN = new RegExp(`${TEMPLATE_EXPR_PLACEHOLDER_PREFIX}\\d+__`, 'g');
|
|
195
203
|
const MODULE_PARSER_OPTIONS = {
|
|
196
204
|
lang: 'tsx',
|
|
197
205
|
sourceType: 'module',
|
|
@@ -386,10 +394,47 @@ const extractJsxRoot = (program) => {
|
|
|
386
394
|
}
|
|
387
395
|
throw new Error('[jsx-loader] Expected the template to contain a single JSX root node.');
|
|
388
396
|
};
|
|
389
|
-
const
|
|
397
|
+
const normalizeJsxTextSegments = (value, placeholders) => {
|
|
390
398
|
const collapsed = value.replace(/\r/g, '').replace(/\n\s+/g, ' ');
|
|
391
|
-
const
|
|
392
|
-
|
|
399
|
+
const leadingWhitespace = value.match(/^\s*/)?.[0] ?? '';
|
|
400
|
+
const trailingWhitespace = value.match(/\s*$/)?.[0] ?? '';
|
|
401
|
+
const trimStart = /\n/.test(leadingWhitespace);
|
|
402
|
+
const trimEnd = /\n/.test(trailingWhitespace);
|
|
403
|
+
let normalized = collapsed;
|
|
404
|
+
if (trimStart) {
|
|
405
|
+
normalized = normalized.replace(/^\s+/, '');
|
|
406
|
+
}
|
|
407
|
+
if (trimEnd) {
|
|
408
|
+
normalized = normalized.replace(/\s+$/, '');
|
|
409
|
+
}
|
|
410
|
+
if (normalized.length === 0 || normalized.trim().length === 0) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const segments = [];
|
|
414
|
+
TEMPLATE_EXPR_PLACEHOLDER_PATTERN.lastIndex = 0;
|
|
415
|
+
let cursor = 0;
|
|
416
|
+
let match;
|
|
417
|
+
while ((match = TEMPLATE_EXPR_PLACEHOLDER_PATTERN.exec(normalized))) {
|
|
418
|
+
const index = match.index;
|
|
419
|
+
const slice = normalized.slice(cursor, index);
|
|
420
|
+
if (slice) {
|
|
421
|
+
segments.push({ kind: 'text', value: slice });
|
|
422
|
+
}
|
|
423
|
+
const marker = match[0];
|
|
424
|
+
const expression = placeholders.get(marker);
|
|
425
|
+
if (expression) {
|
|
426
|
+
segments.push({ kind: 'expression', value: expression });
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
segments.push({ kind: 'text', value: marker });
|
|
430
|
+
}
|
|
431
|
+
cursor = index + marker.length;
|
|
432
|
+
}
|
|
433
|
+
const remainder = normalized.slice(cursor);
|
|
434
|
+
if (remainder) {
|
|
435
|
+
segments.push({ kind: 'text', value: remainder });
|
|
436
|
+
}
|
|
437
|
+
return segments;
|
|
393
438
|
};
|
|
394
439
|
const TAG_PLACEHOLDER_PREFIX = '__JSX_LOADER_TAG_EXPR_';
|
|
395
440
|
const buildTemplateSource = (quasis, expressions, source, tag) => {
|
package/dist/react/react-jsx.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseSync } from 'oxc-parser';
|
|
2
|
-
import { buildTemplate, evaluateExpression, extractRootNode, formatParserError, getIdentifierName,
|
|
2
|
+
import { buildTemplate, evaluateExpression, extractRootNode, formatParserError, getIdentifierName, normalizeJsxTextSegments, parserOptions, } from '../runtime/shared.js';
|
|
3
3
|
import { Fragment, createElement, } from 'react';
|
|
4
4
|
const isIterable = (value) => {
|
|
5
5
|
if (!value || typeof value === 'string') {
|
|
@@ -69,10 +69,8 @@ const evaluateReactJsxChildren = (children, ctx) => {
|
|
|
69
69
|
children.forEach(child => {
|
|
70
70
|
switch (child.type) {
|
|
71
71
|
case 'JSXText': {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
resolved.push(text);
|
|
75
|
-
}
|
|
72
|
+
const segments = normalizeJsxTextSegments(child.value, ctx.placeholders);
|
|
73
|
+
segments.forEach(segment => appendReactChild(resolved, segment));
|
|
76
74
|
break;
|
|
77
75
|
}
|
|
78
76
|
case 'JSXExpressionContainer': {
|
package/dist/runtime/shared.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Expression, JSXElement, JSXFragment, JSXIdentifier, JSXMemberExpression, JSXNamespacedName, Program } from '@oxc-project/types';
|
|
2
2
|
import type { OxcError, ParserOptions } from 'oxc-parser';
|
|
3
|
+
export declare const PLACEHOLDER_PREFIX = "__KX_EXPR__";
|
|
4
|
+
export declare const placeholderPattern: RegExp;
|
|
3
5
|
type AnyTemplateFunction = (...args: any[]) => unknown;
|
|
4
6
|
type AnyTemplateConstructor = abstract new (...args: any[]) => unknown;
|
|
5
7
|
export type TemplateComponent = (AnyTemplateFunction | AnyTemplateConstructor) & {
|
|
@@ -29,7 +31,7 @@ type AnyOxcNode = {
|
|
|
29
31
|
[key: string]: unknown;
|
|
30
32
|
};
|
|
31
33
|
export declare const walkAst: (node: unknown, visitor: (target: AnyOxcNode) => void) => void;
|
|
32
|
-
export declare const
|
|
34
|
+
export declare const normalizeJsxTextSegments: (value: string, placeholders: Map<string, unknown>) => unknown[];
|
|
33
35
|
export declare const collectPlaceholderNames: <TComponent extends TemplateComponent>(expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>) => string[];
|
|
34
36
|
export declare const evaluateExpression: <TComponent extends TemplateComponent>(expression: Expression | JSXElement | JSXFragment, ctx: TemplateContext<TComponent>, evaluateJsxNode: (node: JSXElement | JSXFragment) => unknown) => unknown;
|
|
35
37
|
export declare const sanitizeIdentifier: (value: string) => string;
|
package/dist/runtime/shared.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const OPEN_TAG_RE = /<\s*$/;
|
|
2
2
|
const CLOSE_TAG_RE = /<\/\s*$/;
|
|
3
|
-
const PLACEHOLDER_PREFIX = '__KX_EXPR__';
|
|
3
|
+
export const PLACEHOLDER_PREFIX = '__KX_EXPR__';
|
|
4
|
+
export const placeholderPattern = new RegExp(`${PLACEHOLDER_PREFIX}\\d+_\\d+__`, 'g');
|
|
4
5
|
let invocationCounter = 0;
|
|
5
6
|
export const parserOptions = {
|
|
6
7
|
lang: 'jsx',
|
|
@@ -66,10 +67,46 @@ export const walkAst = (node, visitor) => {
|
|
|
66
67
|
}
|
|
67
68
|
});
|
|
68
69
|
};
|
|
69
|
-
export const
|
|
70
|
+
export const normalizeJsxTextSegments = (value, placeholders) => {
|
|
70
71
|
const collapsed = value.replace(/\r/g, '').replace(/\n\s+/g, ' ');
|
|
71
|
-
const
|
|
72
|
-
|
|
72
|
+
const leadingWhitespace = value.match(/^\s*/)?.[0] ?? '';
|
|
73
|
+
const trailingWhitespace = value.match(/\s*$/)?.[0] ?? '';
|
|
74
|
+
const trimStart = /\n/.test(leadingWhitespace);
|
|
75
|
+
const trimEnd = /\n/.test(trailingWhitespace);
|
|
76
|
+
let normalized = collapsed;
|
|
77
|
+
if (trimStart) {
|
|
78
|
+
normalized = normalized.replace(/^\s+/, '');
|
|
79
|
+
}
|
|
80
|
+
if (trimEnd) {
|
|
81
|
+
normalized = normalized.replace(/\s+$/, '');
|
|
82
|
+
}
|
|
83
|
+
if (normalized.length === 0 || normalized.trim().length === 0) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const segments = [];
|
|
87
|
+
placeholderPattern.lastIndex = 0;
|
|
88
|
+
let cursor = 0;
|
|
89
|
+
let match;
|
|
90
|
+
while ((match = placeholderPattern.exec(normalized))) {
|
|
91
|
+
const index = match.index;
|
|
92
|
+
const slice = normalized.slice(cursor, index);
|
|
93
|
+
if (slice) {
|
|
94
|
+
segments.push(slice);
|
|
95
|
+
}
|
|
96
|
+
const token = match[0];
|
|
97
|
+
if (placeholders.has(token)) {
|
|
98
|
+
segments.push(placeholders.get(token));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
segments.push(token);
|
|
102
|
+
}
|
|
103
|
+
cursor = index + token.length;
|
|
104
|
+
}
|
|
105
|
+
const remainder = normalized.slice(cursor);
|
|
106
|
+
if (remainder) {
|
|
107
|
+
segments.push(remainder);
|
|
108
|
+
}
|
|
109
|
+
return segments;
|
|
73
110
|
};
|
|
74
111
|
export const collectPlaceholderNames = (expression, ctx) => {
|
|
75
112
|
const placeholders = new Set();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/jsx",
|
|
3
|
-
"version": "1.2.0
|
|
3
|
+
"version": "1.2.0",
|
|
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",
|
|
@@ -65,17 +65,19 @@
|
|
|
65
65
|
"./package.json": "./package.json"
|
|
66
66
|
},
|
|
67
67
|
"engines": {
|
|
68
|
-
"node": ">=22"
|
|
68
|
+
"node": ">=22.3.0"
|
|
69
69
|
},
|
|
70
70
|
"engineStrict": true,
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "duel && npm run build:lite",
|
|
73
|
+
"prepare": "husky",
|
|
73
74
|
"precheck-types": "npm run build",
|
|
74
75
|
"check-types": "npm run check-types:lib && npm run check-types:demo",
|
|
75
76
|
"check-types:lib": "tsc --noEmit --project tsconfig.json",
|
|
76
77
|
"check-types:demo": "tsc --noEmit --project examples/browser/tsconfig.json",
|
|
77
78
|
"lint": "eslint src test",
|
|
78
79
|
"prettier": "prettier -w .",
|
|
80
|
+
"prettier:check": "prettier --check .",
|
|
79
81
|
"test": "vitest run --coverage",
|
|
80
82
|
"test:watch": "vitest",
|
|
81
83
|
"test:e2e": "npm run build && npm run setup:wasm && npm run build:fixture && playwright test",
|
|
@@ -103,7 +105,9 @@
|
|
|
103
105
|
"eslint-plugin-n": "^17.10.3",
|
|
104
106
|
"eslint-plugin-playwright": "^2.4.0",
|
|
105
107
|
"http-server": "^14.1.1",
|
|
108
|
+
"husky": "^9.1.7",
|
|
106
109
|
"jsdom": "^27.2.0",
|
|
110
|
+
"lint-staged": "^16.2.7",
|
|
107
111
|
"lit": "^3.2.1",
|
|
108
112
|
"next": "^16.0.0",
|
|
109
113
|
"prettier": "^3.7.3",
|
|
@@ -158,5 +162,9 @@
|
|
|
158
162
|
"printWidth": 90,
|
|
159
163
|
"semi": false,
|
|
160
164
|
"singleQuote": true
|
|
165
|
+
},
|
|
166
|
+
"lint-staged": {
|
|
167
|
+
"*.{js,jsx,ts,tsx,mjs,cjs,cts,mts}": "eslint --max-warnings=0",
|
|
168
|
+
"*.{js,jsx,ts,tsx,mjs,cjs,cts,mts,json,md,css,scss,html}": "prettier --check"
|
|
161
169
|
}
|
|
162
170
|
}
|