Returno to inytegrate Tags in Core
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s
All checks were successful
Deploy Docs to Synology / deploy (push) Successful in 4s
This commit is contained in:
@@ -111,8 +111,9 @@ export const filteredTodos = $(() => {
|
||||
|
||||
```javascript
|
||||
// components/TodoApp.js
|
||||
import { sigpro } from 'sigpro';
|
||||
import { todos, filter, addTodo, toggleTodo, filteredTodos } from "../store/todos.js";
|
||||
import "sigpro/tags" // tags helpers available in global
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
const TodoApp = () =>
|
||||
div({ class: "todo-app" }, [
|
||||
|
||||
@@ -138,7 +138,8 @@ h('svg', { width: 100, height: 100 }, [
|
||||
## Complete Example
|
||||
|
||||
```javascript
|
||||
import { $, h, mount } from 'sigpro';
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
const dynamicTag = $('h1');
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ When `destroy()` is called (or when a new mount replaces an old one), everything
|
||||
## Complete Example
|
||||
|
||||
```javascript
|
||||
import { $, mount, div, h1, button } from 'sigpro';
|
||||
import "sigpro/tags" // tags helpers available in global
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
const App = () => {
|
||||
const count = $(0);
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
# SigPro – Complete API Reference
|
||||
|
||||
SigPro is a **Real‑DOM first** reactive micro‑framework. No virtual DOM, no diffing overhead – it updates the DOM directly with surgical precision. Built‑in automatic cleanup prevents memory leaks, and the API is designed to be both tiny and powerful.
|
||||
|
||||
```javascript
|
||||
import { $, $$, watch, h, when, each, router, mount, batch } from 'sigpro'
|
||||
// Optional side‑effects (activate global helpers & security):
|
||||
import 'sigpro/tags' // → window.div, window.span, etc.
|
||||
import 'sigpro/xss' // → attribute sanitisation
|
||||
```
|
||||
|
||||
In a classic IIFE script (`<script src="sigpro.min.js">`), **everything** (core, router, tags, XSS, global functions) is available automatically.
|
||||
|
||||
---
|
||||
|
||||
## Core Reactivity
|
||||
|
||||
### `$(value, localStorageKey?)` – Signal & Computed
|
||||
@@ -113,7 +100,8 @@ h('div', {}, [
|
||||
Tag helpers are **not exported individually** from the core. To use them globally without the `h(...)` wrapper, activate the side‑effect module:
|
||||
|
||||
```javascript
|
||||
import 'sigpro/tags' // now window.div, window.span, etc. are available
|
||||
import { sigpro } from "sigpro"
|
||||
sigpro(); // now window.div, window.span, etc. are available
|
||||
|
||||
// You can now write:
|
||||
div({ class: 'container' }, [
|
||||
@@ -128,21 +116,6 @@ Available tags: `a`, `abbr`, `article`, `aside`, `audio`, `b`, `blockquote`, `br
|
||||
|
||||
---
|
||||
|
||||
## Built‑in XSS Shield (Optional)
|
||||
|
||||
SigPro includes an optional security layer that sanitises dangerous attributes (`href`, `src`, `formaction`, etc.) and blocks `javascript:` / `data:` / `vbscript:` protocols.
|
||||
To enable it in ESM environments:
|
||||
|
||||
```javascript
|
||||
import 'sigpro/xss'
|
||||
```
|
||||
|
||||
In the IIFE bundle, the shield is **active by default**.
|
||||
|
||||
When the shield is enabled, trying to set `href="javascript:alert(1)"` will log a warning and replace the value with `'#'`.
|
||||
|
||||
---
|
||||
|
||||
## Flow Control Components
|
||||
|
||||
### `when(condition, thenComponent, elseComponent?)`
|
||||
@@ -252,9 +225,8 @@ You never need to manually clean up – just write reactive code.
|
||||
## Full Example – Counter with Persistence
|
||||
|
||||
```javascript
|
||||
import { $, watch, mount } from 'sigpro'
|
||||
import 'sigpro/tags' // ← activate global helpers
|
||||
import 'sigpro/xss' // ← activate security (optional)
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // All in global window
|
||||
|
||||
const count = $(0, 'counter') // persists in localStorage
|
||||
|
||||
@@ -268,19 +240,3 @@ const App = () =>
|
||||
|
||||
mount(App, '#app')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Customising the API (Renaming)
|
||||
|
||||
You can rename everything in one line:
|
||||
|
||||
```javascript
|
||||
import { $ as signal, watch as effect, h as element, mount as render } from 'sigpro'
|
||||
```
|
||||
|
||||
In the IIFE bundle, the core functions are already available as both `window.$` and `window.SigPro.$`, so you can alias them globally if needed:
|
||||
|
||||
```javascript
|
||||
window.myReactive = $ // or window.SigPro.$
|
||||
```
|
||||
@@ -152,8 +152,8 @@ If you want the router outlet to have no layout impact, you can set `display: co
|
||||
## Complete Example
|
||||
|
||||
```javascript
|
||||
import { $, router, mount } from "sigpro";
|
||||
import "sigpro/tags" // tags helpers available in global
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
const Home = () => div("Welcome home");
|
||||
const About = () => div("About us");
|
||||
|
||||
@@ -32,21 +32,13 @@ When you use the **IIFE bundle** (`sigpro.js` or `sigpro.min.js`) with a traditi
|
||||
When you import the **ES module** (`import { ... } from 'sigpro'`), the core **does not** add helpers to `window` by default. To enable global tags, import the dedicated side‑effect module:
|
||||
|
||||
```js
|
||||
import 'sigpro/tags'; // ← activates window.div, window.span, etc.
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
// Now you can use helpers globally
|
||||
const App = () => div({ class: "app" }, "Ready!");
|
||||
```
|
||||
|
||||
If you also want built‑in **XSS protection**, enable it once:
|
||||
|
||||
```js
|
||||
import 'sigpro/xss'; // ← add security layer
|
||||
import 'sigpro/tags'; // ← global helpers
|
||||
```
|
||||
|
||||
Both are side‑effect modules, so the order doesn’t matter.
|
||||
|
||||
> **Important:** The tag helpers are **not** exported as individual named exports from the core (`sigpro`). They become available as global functions (`window.div`, etc.) after the side‑effect runs.
|
||||
> If you prefer to avoid globals, you can always use `h('div', ...)` directly—it’s perfectly fine.
|
||||
|
||||
@@ -201,12 +193,8 @@ const Timer = () => {
|
||||
### ESM (modern projects)
|
||||
|
||||
```javascript
|
||||
// Enable global helpers + security
|
||||
import 'sigpro/tags';
|
||||
import 'sigpro/xss';
|
||||
|
||||
// Import core functions
|
||||
import { $, mount } from 'sigpro';
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // tags helpers available in global also core functions
|
||||
|
||||
const nameSignal = $('');
|
||||
|
||||
@@ -240,26 +228,3 @@ mount(App, '#app');
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Important Notes
|
||||
|
||||
- **Naming:** All tag helpers are **lowercase**.
|
||||
- **Global availability:**
|
||||
- **IIFE script** – automatically on `window`.
|
||||
- **ESM module** – not global by default; use `import 'sigpro/tags'` to activate them.
|
||||
- **Custom components:** Use **PascalCase** for your own component functions (e.g., `UserCard`) to visually distinguish them from built‑in tags.
|
||||
|
||||
---
|
||||
|
||||
## 10. Summary
|
||||
|
||||
| Feature | Description |
|
||||
| :--- | :--- |
|
||||
| **Tag helpers** | Lowercase functions for every HTML element (e.g., `div()`, `button()`). |
|
||||
| **Activation** | IIFE: automatic. ESM: `import 'sigpro/tags'`. |
|
||||
| **Reactive attributes** | Pass a function to any attribute to keep it synced. |
|
||||
| **Two‑way binding** | Assign a signal directly to `value` or `checked` on form elements. |
|
||||
| **Dynamic children** | Pass a function as a child for live updating content. |
|
||||
| **Auto‑cleanup** | All effects, events, and children are disposed when the element is removed. |
|
||||
| **Security** | Optional XSS shield: `import 'sigpro/xss'`. |
|
||||
|
||||
@@ -48,16 +48,13 @@ bun add sigpro
|
||||
|
||||
```html
|
||||
<script type="module">
|
||||
// Import the core – no global helpers or XSS yet
|
||||
import { $, h, mount } from 'https://cdn.jsdelivr.net/npm/sigpro@1.2.23/dist/sigpro.esm.min.js';
|
||||
// Import the core
|
||||
import { sigpro, $, h, mount } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/dist/sigpro.esm.min.js';
|
||||
sigpro(); // Optional: now div, span, button, etc. become global
|
||||
|
||||
// Option A: use named imports (no globals, recommended)
|
||||
const count = $(0);
|
||||
mount(() => h('h1', {}, () => `Count: ${count()}`), '#app');
|
||||
|
||||
// Option B: enable global tag helpers (still optional)
|
||||
import 'https://cdn.jsdelivr.net/npm/sigpro@1.2.23/sigpro/tags.js';
|
||||
// now div, span, button, etc. become global
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -68,7 +65,7 @@ bun add sigpro
|
||||
|
||||
```html
|
||||
<!-- Classic script: full kit (core, router, tags, XSS) auto‑installed -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@1.2.23/dist/sigpro.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sigpro@latest/dist/sigpro.min.js"></script>
|
||||
<script>
|
||||
// $, h, div, button, router, etc. are already global
|
||||
const count = $(0);
|
||||
@@ -95,9 +92,8 @@ SigPro uses **lowercase** Tag Helpers (e.g., `div`, `button`) to keep the syntax
|
||||
|
||||
```javascript
|
||||
// App.js – Use named imports for the core, activate helpers if needed
|
||||
import { $, mount } from 'sigpro';
|
||||
import 'sigpro/tags'; // ← make div, h1, button, etc. global
|
||||
import 'sigpro/xss'; // ← optional: activate XSS shield
|
||||
import { sigpro, $, mount } from 'sigpro';
|
||||
sigpro(); // Optional: now div, span, button, etc. become global
|
||||
|
||||
const App = () => {
|
||||
const count = $(0);
|
||||
@@ -171,12 +167,8 @@ When you import the **ESM core** (`import { ... } from 'sigpro'`), **only the re
|
||||
|
||||
2. **Activate global helpers** (when you want `div`, `span`, etc. without importing each one):
|
||||
```javascript
|
||||
import 'sigpro/tags'; // side‑effect: injects all tag helpers into window
|
||||
```
|
||||
|
||||
3. **Activate XSS shield** (optional):
|
||||
```javascript
|
||||
import 'sigpro/xss'; // side‑effect: enables attribute sanitization
|
||||
import { sigpro } from 'sigpro';
|
||||
sigpro(); // side‑effect: injects all tag helpers into window
|
||||
```
|
||||
|
||||
### Why two modes?
|
||||
|
||||
@@ -42,12 +42,9 @@
|
||||
__export(exports_sigpro, {
|
||||
when: () => when,
|
||||
watch: () => watch,
|
||||
setAttrFilter: () => setAttrFilter,
|
||||
sigpro: () => sigpro,
|
||||
router: () => router,
|
||||
render: () => render,
|
||||
onUnmount: () => onUnmount,
|
||||
mount: () => mount,
|
||||
isFunc: () => isFunc,
|
||||
h: () => h,
|
||||
each: () => each,
|
||||
batch: () => batch,
|
||||
@@ -72,10 +69,6 @@
|
||||
var SVG_NS = "http://www.w3.org/2000/svg";
|
||||
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
||||
var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(","));
|
||||
var attrFilter = null;
|
||||
var setAttrFilter = (fn) => {
|
||||
attrFilter = fn;
|
||||
};
|
||||
var dispose = (eff) => {
|
||||
if (!eff || eff._disposed)
|
||||
return;
|
||||
@@ -304,6 +297,21 @@
|
||||
if (node.childNodes)
|
||||
node.childNodes.forEach((n) => cleanupNode(n));
|
||||
};
|
||||
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
|
||||
var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]);
|
||||
var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on");
|
||||
var validateAttr = (key, val) => {
|
||||
if (val == null || val === false)
|
||||
return null;
|
||||
if (isDangerousAttr(key)) {
|
||||
const sVal = String(val);
|
||||
if (DANGEROUS_PROTOCOL.test(sVal)) {
|
||||
console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`);
|
||||
return "#";
|
||||
}
|
||||
}
|
||||
return val;
|
||||
};
|
||||
var h = (tag, props = {}, children = []) => {
|
||||
if (props instanceof Node || isArr(props) || !isObj(props)) {
|
||||
children = props;
|
||||
@@ -342,38 +350,38 @@
|
||||
isFunc(v) ? v(el) : v.current = el;
|
||||
continue;
|
||||
}
|
||||
let val = attrFilter ? attrFilter(k, v) : v;
|
||||
if (isSVG && k.startsWith("xlink:")) {
|
||||
val == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), val);
|
||||
const cleanVal = validateAttr(k.slice(6), v);
|
||||
cleanVal == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal);
|
||||
continue;
|
||||
}
|
||||
if (k.startsWith("on")) {
|
||||
const ev = k.slice(2).toLowerCase();
|
||||
el.addEventListener(ev, val);
|
||||
const off = () => el.removeEventListener(ev, val);
|
||||
el.addEventListener(ev, v);
|
||||
const off = () => el.removeEventListener(ev, v);
|
||||
el._cleanups.add(off);
|
||||
onUnmount(off);
|
||||
} else if (isFunc(val)) {
|
||||
} else if (isFunc(v)) {
|
||||
const effect = createEffect(() => {
|
||||
const raw = val();
|
||||
const safeVal = attrFilter ? attrFilter(k, raw) : raw;
|
||||
const val = validateAttr(k, v());
|
||||
if (k === "class")
|
||||
el.className = safeVal || "";
|
||||
else if (safeVal == null)
|
||||
el.className = val || "";
|
||||
else if (val == null)
|
||||
el.removeAttribute(k);
|
||||
else if (k in el && !isSVG)
|
||||
el[k] = safeVal;
|
||||
el[k] = val;
|
||||
else
|
||||
el.setAttribute(k, safeVal === true ? "" : safeVal);
|
||||
el.setAttribute(k, val === true ? "" : val);
|
||||
});
|
||||
effect();
|
||||
el._cleanups.add(() => dispose(effect));
|
||||
onUnmount(() => dispose(effect));
|
||||
if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) {
|
||||
const evType = k === "checked" ? "change" : "input";
|
||||
el.addEventListener(evType, (ev) => val(ev.target[k]));
|
||||
el.addEventListener(evType, (ev) => v(ev.target[k]));
|
||||
}
|
||||
} else {
|
||||
const val = validateAttr(k, v);
|
||||
if (val != null) {
|
||||
if (k in el && !isSVG)
|
||||
el[k] = val;
|
||||
@@ -555,4 +563,14 @@
|
||||
MOUNTED_NODES.set(t, inst);
|
||||
return inst;
|
||||
};
|
||||
var sigpro = () => {
|
||||
if (typeof window === "undefined")
|
||||
return;
|
||||
Object.assign(window, { $, $$, watch, h, when, each, router, mount, batch });
|
||||
"a abbr article aside ... video".split(" ").forEach((tag) => {
|
||||
window[tag] = (props, children) => h(tag, props, children);
|
||||
});
|
||||
};
|
||||
if (typeof import.meta === "undefined" && typeof window !== "undefined")
|
||||
sigpro();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user