ilha 0.5.0 → 0.6.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/dist/index.d.ts CHANGED
@@ -55,6 +55,14 @@ interface MarkedSignalAccessor<T> {
55
55
  }
56
56
  declare function ilhaRaw(value: string): RawHtml;
57
57
  declare function ilhaCss(strings: TemplateStringsArray | string, ...values: (string | number)[]): string;
58
+ type JsxChild = unknown;
59
+ type JsxProps = Record<string, unknown> | null | undefined;
60
+ type JsxType = string | ((props: Record<string, unknown>) => unknown);
61
+ declare function ilhaJsx(type: JsxType, props: JsxProps, ...children: JsxChild[]): RawHtml;
62
+ declare function ilhaFragment(props: {
63
+ children?: JsxChild;
64
+ } | null, ...children: JsxChild[]): RawHtml;
65
+ declare function ilhaJsxDEV(type: JsxType, props: JsxProps): RawHtml;
58
66
  declare function ilhaHtml(strings: TemplateStringsArray, ...values: unknown[]): RawHtml;
59
67
  type ContextSignal<T> = {
60
68
  (): T;
@@ -269,6 +277,10 @@ declare function mountAll(registry: IslandRegistry, options?: MountOptions): Mou
269
277
  declare const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, Record<never, never>> & {
270
278
  html: typeof ilhaHtml;
271
279
  raw: typeof ilhaRaw;
280
+ jsx: typeof ilhaJsx;
281
+ jsxs: typeof ilhaJsx;
282
+ jsxDEV: typeof ilhaJsxDEV;
283
+ Fragment: typeof ilhaFragment;
272
284
  mount: typeof mountAll;
273
285
  from: typeof ilhaFrom;
274
286
  context: typeof ilhaContext;
@@ -278,9 +290,13 @@ declare const ilha: IlhaBuilder<Record<string, unknown>, Record<never, never>, R
278
290
  };
279
291
  declare const html: typeof ilhaHtml;
280
292
  declare const raw: typeof ilhaRaw;
293
+ declare const jsx: typeof ilhaJsx;
294
+ declare const jsxs: typeof ilhaJsx;
295
+ declare const jsxDEV: typeof ilhaJsxDEV;
296
+ declare const Fragment: typeof ilhaFragment;
281
297
  declare const css: typeof ilhaCss;
282
298
  declare const mount: typeof mountAll;
283
299
  declare const from: typeof ilhaFrom;
284
300
  declare const context: typeof ilhaContext;
285
301
  //#endregion
286
- export { DerivedValue, ErrorContext, ErrorSource, ExternalSignal, HandlerContext, HandlerContextFor, HydratableOptions, ISLAND_MOUNT_INTERNAL, Island, IslandDerived, IslandState, KeyedIsland, MountOptions, MountResult, OnMountContext, RawHtml, SignalAccessor, batch, context, css, ilha as default, from, html, ilhaSignal, ilhaSignal as signal, mount, raw, untrack };
302
+ export { DerivedValue, ErrorContext, ErrorSource, ExternalSignal, Fragment, HandlerContext, HandlerContextFor, HydratableOptions, ISLAND_MOUNT_INTERNAL, Island, IslandDerived, IslandState, KeyedIsland, MountOptions, MountResult, OnMountContext, RawHtml, SignalAccessor, batch, context, css, ilha as default, from, html, ilhaSignal, ilhaSignal as signal, jsx, jsxDEV, jsxs, mount, raw, untrack };
package/dist/index.js CHANGED
@@ -191,8 +191,120 @@ function ilhaCss(strings, ...values) {
191
191
  }
192
192
  return result;
193
193
  }
194
+ const SAFE_NAME_RE = /^[A-Za-z_:][A-Za-z0-9:._-]*$/;
195
+ const SAFE_BIND_LOCAL_RE = /^[A-Za-z][A-Za-z0-9]*$/;
196
+ const VOID_ELEMENTS = new Set([
197
+ "area",
198
+ "base",
199
+ "br",
200
+ "col",
201
+ "embed",
202
+ "hr",
203
+ "img",
204
+ "input",
205
+ "link",
206
+ "meta",
207
+ "param",
208
+ "source",
209
+ "track",
210
+ "wbr"
211
+ ]);
212
+ const SAFE_CSS_PROP_RE = /^(-{2}[a-zA-Z][a-zA-Z0-9-]*|-?[a-zA-Z][a-zA-Z0-9-]*)$/;
213
+ const URL_ATTRS = new Set([
214
+ "href",
215
+ "src",
216
+ "action",
217
+ "formaction",
218
+ "cite",
219
+ "data",
220
+ "poster"
221
+ ]);
222
+ const SAFE_URL_RE = /^(?!javascript:|data:text\/html|data:text\/xml|data:application\/xhtml\+xml|data:image\/svg|vbscript:)/i;
223
+ function normalizeClass(value) {
224
+ if (Array.isArray(value)) return value.filter(Boolean).join(" ");
225
+ if (value && typeof value === "object") return Object.entries(value).filter(([, enabled]) => Boolean(enabled)).map(([name]) => name).join(" ");
226
+ return String(value);
227
+ }
228
+ function normalizeJsxChildren(props, children) {
229
+ const propChildren = props && "children" in props ? props.children : void 0;
230
+ return (children.length > 0 ? children : propChildren === void 0 ? [] : [propChildren]).flat(1);
231
+ }
232
+ function serializeStyle(value) {
233
+ return Object.entries(value).map(([k, v]) => {
234
+ if (!SAFE_CSS_PROP_RE.test(k)) return "";
235
+ const prop = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
236
+ let safeV = String(v).replace(/["<>{};]/g, "");
237
+ if (/expression\(/i.test(safeV) || /^javascript:/i.test(safeV)) return "";
238
+ return `${prop}:${safeV}`;
239
+ }).filter(Boolean).join(";");
240
+ }
241
+ function pushJsxAttr(chunks, values, name, value) {
242
+ if (value == null || value === false || name === "children" || name === "key") return;
243
+ if (name === "__proto__" || name === "constructor" || name === "prototype") return;
244
+ if (name.startsWith("bind:")) {
245
+ const [prefix, localName, ...rest] = name.split(":");
246
+ if (prefix !== "bind" || rest.length > 0 || !localName || !SAFE_BIND_LOCAL_RE.test(localName)) return;
247
+ if (!isSignalAccessor(value)) return;
248
+ const safeName = `${prefix}:${localName}`;
249
+ chunks[chunks.length - 1] += ` ${safeName}=`;
250
+ values.push(value);
251
+ chunks.push("");
252
+ return;
253
+ }
254
+ if (!SAFE_NAME_RE.test(name)) return;
255
+ let safeName = name;
256
+ if (safeName === "className") safeName = "class";
257
+ if (safeName === "htmlFor") safeName = "for";
258
+ if (safeName.startsWith("on")) return;
259
+ if (safeName === "class") value = normalizeClass(value);
260
+ if (safeName === "style" && value && typeof value === "object") value = serializeStyle(value);
261
+ if ((URL_ATTRS.has(safeName) || /:(href|src|action|formaction|cite|data|poster)$/.test(safeName)) && typeof value === "string" && !SAFE_URL_RE.test(value.trim())) return;
262
+ if (value === true) {
263
+ chunks[chunks.length - 1] += ` ${safeName}`;
264
+ return;
265
+ }
266
+ chunks[chunks.length - 1] += ` ${safeName}="`;
267
+ values.push(value);
268
+ chunks.push("\"");
269
+ }
270
+ function renderJsxElement(type, props, children) {
271
+ const chunks = [`<${type}`];
272
+ const values = [];
273
+ if (props) for (const [name, value] of Object.entries(props)) pushJsxAttr(chunks, values, name, value);
274
+ chunks[chunks.length - 1] += ">";
275
+ if (!VOID_ELEMENTS.has(type)) {
276
+ for (const child of children) {
277
+ values.push(child);
278
+ chunks.push("");
279
+ }
280
+ chunks[chunks.length - 1] += `</${type}>`;
281
+ } else if (children.length > 0 && __DEV__) warn(`Void element <${type}> should not have children. They will be ignored.`);
282
+ return ilhaHtml(chunks, ...values);
283
+ }
284
+ function ilhaJsx(type, props, ...children) {
285
+ const normalizedChildren = normalizeJsxChildren(props, children);
286
+ if (typeof type === "function") {
287
+ const componentProps = {
288
+ ...props ?? {},
289
+ ...normalizedChildren.length > 0 ? { children: normalizedChildren } : {}
290
+ };
291
+ delete componentProps["key"];
292
+ const out = type(Object.keys(componentProps).length ? componentProps : {});
293
+ if (typeof out === "string") return ilhaHtml`${out}`;
294
+ if (out && typeof out === "object" && RAW in out) return out;
295
+ return ilhaHtml`${out}`;
296
+ }
297
+ return renderJsxElement(type, props, normalizedChildren);
298
+ }
299
+ function ilhaFragment(props, ...children) {
300
+ const normalizedChildren = normalizeJsxChildren(props, children);
301
+ return ilhaHtml(["", ...normalizedChildren.map(() => "")], ...normalizedChildren);
302
+ }
303
+ function ilhaJsxDEV(type, props) {
304
+ return ilhaJsx(type, props);
305
+ }
194
306
  function interpolateValue(v) {
195
- if (v == null) return "";
307
+ if (v == null || v === true || v === false) return "";
196
308
  if (Array.isArray(v)) return v.map(interpolateValue).join("");
197
309
  if (typeof v === "object" && RAW in v) return v.value;
198
310
  if (isIslandCall(v)) {
@@ -216,7 +328,11 @@ const BIND_VALID_KINDS = new Set([
216
328
  ]);
217
329
  const BIND_PREFIX_RE = /\bbind:([a-zA-Z]+)\s*=\s*("|')?$/;
218
330
  function formatDateForInput(d) {
219
- if (d instanceof Date && !isNaN(d.getTime())) {
331
+ if (d instanceof Date) {
332
+ if (isNaN(d.getTime())) {
333
+ if (__DEV__) warn("bind:valueAsDate received an invalid Date object — value attribute will be empty.");
334
+ return "";
335
+ }
220
336
  const pad = (n) => String(n).padStart(2, "0");
221
337
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
222
338
  }
@@ -1469,6 +1585,10 @@ const rootBuilder = new IlhaBuilder({
1469
1585
  const ilha = Object.assign(rootBuilder, {
1470
1586
  html: ilhaHtml,
1471
1587
  raw: ilhaRaw,
1588
+ jsx: ilhaJsx,
1589
+ jsxs: ilhaJsx,
1590
+ jsxDEV: ilhaJsxDEV,
1591
+ Fragment: ilhaFragment,
1472
1592
  mount: mountAll,
1473
1593
  from: ilhaFrom,
1474
1594
  context: ilhaContext,
@@ -1478,9 +1598,13 @@ const ilha = Object.assign(rootBuilder, {
1478
1598
  });
1479
1599
  const html = ilhaHtml;
1480
1600
  const raw = ilhaRaw;
1601
+ const jsx = ilhaJsx;
1602
+ const jsxs = ilhaJsx;
1603
+ const jsxDEV = ilhaJsxDEV;
1604
+ const Fragment = ilhaFragment;
1481
1605
  const css = ilhaCss;
1482
1606
  const mount = mountAll;
1483
1607
  const from = ilhaFrom;
1484
1608
  const context = ilhaContext;
1485
1609
  //#endregion
1486
- export { ISLAND_MOUNT_INTERNAL, batch, context, css, ilha as default, from, html, ilhaSignal, ilhaSignal as signal, mount, raw, untrack };
1610
+ export { Fragment, ISLAND_MOUNT_INTERNAL, batch, context, css, ilha as default, from, html, ilhaSignal, ilhaSignal as signal, jsx, jsxDEV, jsxs, mount, raw, untrack };
@@ -0,0 +1,3 @@
1
+ import { Fragment, jsxDEV } from "./index.js";
2
+ import { t as JSX } from "./jsx-types-DSrM2NdI.js";
3
+ export { Fragment, type JSX, jsxDEV };
@@ -0,0 +1,2 @@
1
+ import { Fragment, jsxDEV } from "./index.js";
2
+ export { Fragment, jsxDEV };
@@ -0,0 +1,3 @@
1
+ import { Fragment, jsx, jsxs } from "./index.js";
2
+ import { t as JSX } from "./jsx-types-DSrM2NdI.js";
3
+ export { Fragment, type JSX, jsx, jsxs };
@@ -0,0 +1,2 @@
1
+ import { Fragment, jsx, jsxs } from "./index.js";
2
+ export { Fragment, jsx, jsxs };
@@ -0,0 +1,26 @@
1
+ import { RawHtml } from "./index.js";
2
+
3
+ //#region src/jsx-types.d.ts
4
+ declare namespace JSX {
5
+ type Child = unknown;
6
+ type ElementType = string | ((props: any) => any);
7
+ interface Element extends RawHtml {}
8
+ interface ElementChildrenAttribute {
9
+ children: {};
10
+ }
11
+ interface IntrinsicAttributes {
12
+ key?: string | number;
13
+ }
14
+ type IntrinsicElementProps = {
15
+ children?: Child;
16
+ class?: string | unknown[] | Record<string, boolean>;
17
+ className?: string | unknown[] | Record<string, boolean>;
18
+ htmlFor?: string;
19
+ [name: string]: unknown;
20
+ };
21
+ interface IntrinsicElements {
22
+ [name: string]: IntrinsicElementProps;
23
+ }
24
+ }
25
+ //#endregion
26
+ export { JSX as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilha",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A tiny, framework-free island architecture library",
5
5
  "license": "MIT",
6
6
  "author": "Ryuz <ryuzer@proton.me>",
@@ -19,6 +19,14 @@
19
19
  ".": {
20
20
  "types": "./dist/index.d.ts",
21
21
  "import": "./dist/index.js"
22
+ },
23
+ "./jsx-runtime": {
24
+ "types": "./dist/jsx-runtime.d.ts",
25
+ "import": "./dist/jsx-runtime.js"
26
+ },
27
+ "./jsx-dev-runtime": {
28
+ "types": "./dist/jsx-dev-runtime.d.ts",
29
+ "import": "./dist/jsx-dev-runtime.js"
22
30
  }
23
31
  },
24
32
  "scripts": {