improve docs
This commit is contained in:
30
package.json
30
package.json
@@ -4,10 +4,34 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./index.js",
|
".": {
|
||||||
"./plugins": "./plugins/index.js",
|
"import": "./index.js",
|
||||||
"./vite/*": "./vite/*.js"
|
"types": "./sigpro/sigpro.d.ts"
|
||||||
},
|
},
|
||||||
|
"./plugins": {
|
||||||
|
"import": "./plugins/index.js",
|
||||||
|
"types": "./plugins/index.d.ts"
|
||||||
|
},
|
||||||
|
"./vite/*": {
|
||||||
|
"import": "./vite/*.js",
|
||||||
|
"types": "./vite/*.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"types": "./sigpro/sigpro.d.ts",
|
||||||
|
"typesVersions": {
|
||||||
|
"*": {
|
||||||
|
"*": ["./sigpro/sigpro.d.ts"],
|
||||||
|
"plugins": ["./plugins/index.d.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"sigpro/",
|
||||||
|
"plugins/",
|
||||||
|
"vite/",
|
||||||
|
"README.md",
|
||||||
|
"LICENSE"
|
||||||
|
],
|
||||||
"homepage": "https://natxocc.github.io/sigpro/",
|
"homepage": "https://natxocc.github.io/sigpro/",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
106
plugins/index.d.ts
vendored
Normal file
106
plugins/index.d.ts
vendored
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* SigPro Plugins
|
||||||
|
* Official plugins for SigPro reactive framework
|
||||||
|
*
|
||||||
|
* @module sigpro/plugins
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { SigPro } from '../sigpro/sigpro';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin context passed to all plugins
|
||||||
|
*/
|
||||||
|
export interface PluginContext {
|
||||||
|
$: SigPro;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base plugin interface
|
||||||
|
*/
|
||||||
|
export interface SigProPlugin {
|
||||||
|
/** Plugin name for debugging */
|
||||||
|
name: string;
|
||||||
|
/** Plugin version */
|
||||||
|
version?: string;
|
||||||
|
/** Initialize plugin with SigPro instance */
|
||||||
|
install: (context: PluginContext) => void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI Plugin - Creates reactive UI components
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* import { UI } from 'sigpro/plugins';
|
||||||
|
*
|
||||||
|
* $.plugin(UI);
|
||||||
|
*
|
||||||
|
* // Now you can use UI components
|
||||||
|
* const modal = UI.modal({
|
||||||
|
* title: 'Hello',
|
||||||
|
* content: 'This is a modal'
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const UI: SigProPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch Plugin - Reactive data fetching
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* import { Fetch } from 'sigpro/plugins';
|
||||||
|
*
|
||||||
|
* $.plugin(Fetch);
|
||||||
|
*
|
||||||
|
* // Reactive data fetching
|
||||||
|
* const users = $.fetch('/api/users');
|
||||||
|
* const user = $.fetch(() => `/api/users/${userId()}`);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const Fetch: SigProPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug Plugin - Development tools and logging
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* import { Debug } from 'sigpro/plugins';
|
||||||
|
*
|
||||||
|
* $.plugin(Debug);
|
||||||
|
*
|
||||||
|
* // Debug signals in console
|
||||||
|
* $.debug(count); // Logs changes to console
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const Debug: SigProPlugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin options for each plugin
|
||||||
|
*/
|
||||||
|
export namespace PluginOptions {
|
||||||
|
interface UIOptions {
|
||||||
|
/** Prefix for CSS classes */
|
||||||
|
classPrefix?: string;
|
||||||
|
/** Default animation duration */
|
||||||
|
animationDuration?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FetchOptions {
|
||||||
|
/** Base URL for all requests */
|
||||||
|
baseURL?: string;
|
||||||
|
/** Default headers */
|
||||||
|
headers?: Record<string, string>;
|
||||||
|
/** Timeout in milliseconds */
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DebugOptions {
|
||||||
|
/** Enable verbose logging */
|
||||||
|
verbose?: boolean;
|
||||||
|
/** Log to console */
|
||||||
|
logToConsole?: boolean;
|
||||||
|
/** Enable time travel debugging */
|
||||||
|
timeTravel?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
// /plugins/index.js
|
// /plugins/index.js
|
||||||
export { UI } from './ui.js';
|
export { UI } from './ui.js';
|
||||||
export { Fetch } from './fetch.js';
|
export { Fetch } from './fetch.js';
|
||||||
export { Storage } from './storage.js';
|
|
||||||
export { Debug } from './debug.js';
|
export { Debug } from './debug.js';
|
||||||
export { Router } from './router.js';
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
// plugins/router.js
|
|
||||||
export const Router = ($) => {
|
|
||||||
|
|
||||||
$.router = (routes) => {
|
|
||||||
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
|
|
||||||
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
|
|
||||||
|
|
||||||
return div([
|
|
||||||
() => {
|
|
||||||
const current = sPath();
|
|
||||||
const route = routes.find(r => {
|
|
||||||
const rP = r.path.split('/').filter(Boolean);
|
|
||||||
const cP = current.split('/').filter(Boolean);
|
|
||||||
if (rP.length !== cP.length) return false;
|
|
||||||
return rP.every((part, i) => part.startsWith(':') || part === cP[i]);
|
|
||||||
}) || routes.find(r => r.path === "*");
|
|
||||||
|
|
||||||
if (!route) return h1("404 - Not Found");
|
|
||||||
|
|
||||||
// --- LA MEJORA AQUÍ ---
|
|
||||||
const result = typeof route.component === 'function' ? route.component() : route.component;
|
|
||||||
|
|
||||||
// Si el componente es una Promesa (Lazy Loading de Vite), esperamos
|
|
||||||
if (result instanceof Promise) {
|
|
||||||
const $lazyNode = $(span("Cargando página..."));
|
|
||||||
result.then(m => {
|
|
||||||
// Si el módulo tiene un .default (export default), lo usamos
|
|
||||||
const comp = typeof m === 'function' ? m() : (m.default ? m.default() : m);
|
|
||||||
$lazyNode(comp);
|
|
||||||
});
|
|
||||||
return () => $lazyNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result instanceof Node ? result : span(String(result));
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
$.router.go = (path) => {
|
|
||||||
window.location.hash = path.startsWith('/') ? path : `/${path}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
window._router = $.router;
|
|
||||||
};
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* SigPro Storage Plugin
|
|
||||||
* Automatically synchronizes signals with localStorage.
|
|
||||||
*/
|
|
||||||
export const Storage = ($) => {
|
|
||||||
/**
|
|
||||||
* Persists a signal's value in localStorage.
|
|
||||||
* @param {Function} $sig - The signal to persist.
|
|
||||||
* @param {string} key - The localStorage key name.
|
|
||||||
* @returns {Function} The same signal for chaining.
|
|
||||||
*/
|
|
||||||
_storage = ($sig, key) => {
|
|
||||||
// 1. Initial Load: If there's data in storage, update the signal immediately
|
|
||||||
const saved = localStorage.getItem(key);
|
|
||||||
if (saved !== null) {
|
|
||||||
try {
|
|
||||||
$sig(JSON.parse(saved));
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[SigPro Storage] Error parsing key "${key}":`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Auto-Save: Every time the signal changes, update localStorage
|
|
||||||
$(() => {
|
|
||||||
const val = $sig();
|
|
||||||
localStorage.setItem(key, JSON.stringify(val));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $sig;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
387
sigpro/sigpro.d.ts
vendored
Normal file
387
sigpro/sigpro.d.ts
vendored
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/**
|
||||||
|
* SigPro - Atomic Unified Reactive Engine
|
||||||
|
* A lightweight, fine-grained reactivity system with built-in routing and plugin support.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Create a reactive signal
|
||||||
|
* const count = $(0);
|
||||||
|
*
|
||||||
|
* // Create a computed value
|
||||||
|
* const double = $(() => count() * 2);
|
||||||
|
*
|
||||||
|
* // Create reactive HTML
|
||||||
|
* const app = div({ $class: () => count() > 5 ? 'high' : 'low' }, [
|
||||||
|
* h1("Counter Demo"),
|
||||||
|
* p("Count: ", () => count()),
|
||||||
|
* p("Double: ", () => double()),
|
||||||
|
* button({ onclick: () => count(count() + 1) }, "Increment")
|
||||||
|
* ]);
|
||||||
|
*
|
||||||
|
* // Mount to DOM
|
||||||
|
* $.mount(app);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
/** Main SigPro instance */
|
||||||
|
$: SigPro;
|
||||||
|
/** HTML element creators (auto-generated from tags list) */
|
||||||
|
div: typeof html;
|
||||||
|
span: typeof html;
|
||||||
|
p: typeof html;
|
||||||
|
button: typeof html;
|
||||||
|
h1: typeof html;
|
||||||
|
h2: typeof html;
|
||||||
|
h3: typeof html;
|
||||||
|
ul: typeof html;
|
||||||
|
ol: typeof html;
|
||||||
|
li: typeof html;
|
||||||
|
a: typeof html;
|
||||||
|
label: typeof html;
|
||||||
|
section: typeof html;
|
||||||
|
nav: typeof html;
|
||||||
|
main: typeof html;
|
||||||
|
header: typeof html;
|
||||||
|
footer: typeof html;
|
||||||
|
input: typeof html;
|
||||||
|
form: typeof html;
|
||||||
|
img: typeof html;
|
||||||
|
select: typeof html;
|
||||||
|
option: typeof html;
|
||||||
|
table: typeof html;
|
||||||
|
thead: typeof html;
|
||||||
|
tbody: typeof html;
|
||||||
|
tr: typeof html;
|
||||||
|
th: typeof html;
|
||||||
|
td: typeof html;
|
||||||
|
canvas: typeof html;
|
||||||
|
video: typeof html;
|
||||||
|
audio: typeof html;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reactive Signal - A reactive state container
|
||||||
|
* @template T Type of the stored value
|
||||||
|
*/
|
||||||
|
interface Signal<T> {
|
||||||
|
/** Get the current value */
|
||||||
|
(): T;
|
||||||
|
/** Set a new value */
|
||||||
|
(value: T): T;
|
||||||
|
/** Update value based on previous value */
|
||||||
|
(updater: (prev: T) => T): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computed Signal - A reactive derived value
|
||||||
|
* @template T Type of the computed value
|
||||||
|
*/
|
||||||
|
interface Computed<T> {
|
||||||
|
/** Get the current computed value */
|
||||||
|
(): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reactive Effect - A function that re-runs when dependencies change
|
||||||
|
*/
|
||||||
|
type Effect = () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML Content Types
|
||||||
|
*/
|
||||||
|
type HtmlContent =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| Node
|
||||||
|
| HtmlContent[]
|
||||||
|
| (() => string | number | Node | null | undefined);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTML Attributes and Event Handlers
|
||||||
|
*/
|
||||||
|
interface HtmlProps extends Record<string, any> {
|
||||||
|
/** Two-way binding for input values */
|
||||||
|
$value?: Signal<any> | ((val: any) => void);
|
||||||
|
/** Two-way binding for checkbox/radio checked state */
|
||||||
|
$checked?: Signal<boolean> | ((val: boolean) => void);
|
||||||
|
/** Reactive class binding */
|
||||||
|
$class?: string | (() => string);
|
||||||
|
/** Reactive style binding */
|
||||||
|
$style?: string | object | (() => string | object);
|
||||||
|
/** Reactive attribute binding (any attribute can be prefixed with $) */
|
||||||
|
[key: `$${string}`]: any;
|
||||||
|
/** Standard event handlers */
|
||||||
|
onclick?: (event: MouseEvent) => void;
|
||||||
|
oninput?: (event: Event) => void;
|
||||||
|
onchange?: (event: Event) => void;
|
||||||
|
onsubmit?: (event: Event) => void;
|
||||||
|
onkeydown?: (event: KeyboardEvent) => void;
|
||||||
|
onkeyup?: (event: KeyboardEvent) => void;
|
||||||
|
onfocus?: (event: FocusEvent) => void;
|
||||||
|
onblur?: (event: FocusEvent) => void;
|
||||||
|
onmouseover?: (event: MouseEvent) => void;
|
||||||
|
onmouseout?: (event: MouseEvent) => void;
|
||||||
|
[key: `on${string}`]: ((event: any) => void) | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route Configuration
|
||||||
|
*/
|
||||||
|
interface Route {
|
||||||
|
/** URL path pattern (supports :params and * wildcard) */
|
||||||
|
path: string;
|
||||||
|
/** Component to render (can be sync, async, or Promise) */
|
||||||
|
component: ((params: Record<string, string>) => HTMLElement | Promise<HTMLElement>) | Promise<any> | HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router Instance
|
||||||
|
*/
|
||||||
|
interface Router {
|
||||||
|
/** Router container element */
|
||||||
|
(routes: Route[]): HTMLElement;
|
||||||
|
/** Programmatic navigation */
|
||||||
|
go: (path: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin System
|
||||||
|
*/
|
||||||
|
interface Plugin {
|
||||||
|
/**
|
||||||
|
* Extend SigPro with custom functionality or load external scripts
|
||||||
|
* @param source - Plugin function, script URL, or array of URLs
|
||||||
|
* @returns SigPro instance (sync) or Promise (async loading)
|
||||||
|
*/
|
||||||
|
(source: ((sigpro: SigPro) => void) | string | string[]): SigPro | Promise<SigPro>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main SigPro Interface
|
||||||
|
*/
|
||||||
|
interface SigPro {
|
||||||
|
/**
|
||||||
|
* Create a reactive Signal or Computed value
|
||||||
|
* @template T Type of the value
|
||||||
|
* @param initial - Initial value or computed function
|
||||||
|
* @param key - Optional localStorage key for persistence
|
||||||
|
* @returns Reactive signal or computed function
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Signal with localStorage persistence
|
||||||
|
* const count = $(0, 'app.count');
|
||||||
|
*
|
||||||
|
* // Computed value
|
||||||
|
* const double = $(() => count() * 2);
|
||||||
|
*
|
||||||
|
* // Reactive effect (runs automatically)
|
||||||
|
* $(() => console.log('Count changed:', count()));
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
<T>(initial: T, key?: string): Signal<T>;
|
||||||
|
<T>(computed: () => T, key?: string): Computed<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create reactive HTML elements with hyperscript syntax
|
||||||
|
* @param tag - HTML tag name
|
||||||
|
* @param props - Attributes, events, and reactive bindings
|
||||||
|
* @param content - Child nodes or content
|
||||||
|
* @returns Live DOM element with reactivity
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const name = $('World');
|
||||||
|
*
|
||||||
|
* const element = $.html('div',
|
||||||
|
* { class: 'greeting', $class: () => name().length > 5 ? 'long' : '' },
|
||||||
|
* [
|
||||||
|
* $.html('h1', 'Hello'),
|
||||||
|
* $.html('p', () => `Hello, ${name()}!`),
|
||||||
|
* $.html('input', {
|
||||||
|
* $value: name,
|
||||||
|
* placeholder: 'Enter your name'
|
||||||
|
* })
|
||||||
|
* ]
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
html: typeof html;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount a component to the DOM
|
||||||
|
* @param node - Component or element to mount
|
||||||
|
* @param target - Target element or CSS selector (default: document.body)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Mount to body
|
||||||
|
* $.mount(app);
|
||||||
|
*
|
||||||
|
* // Mount to specific element
|
||||||
|
* $.mount(app, '#app');
|
||||||
|
*
|
||||||
|
* // Mount with component function
|
||||||
|
* $.mount(() => div("Dynamic component"));
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
mount: typeof mount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a hash-based router
|
||||||
|
* @param routes - Array of route configurations
|
||||||
|
* @returns Router container element
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const routes = [
|
||||||
|
* { path: '/', component: Home },
|
||||||
|
* { path: '/user/:id', component: UserProfile },
|
||||||
|
* { path: '*', component: NotFound }
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* const router = $.router(routes);
|
||||||
|
* $.mount(router);
|
||||||
|
*
|
||||||
|
* // Navigate programmatically
|
||||||
|
* $.router.go('/user/42');
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
router: Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend SigPro with plugins or load external scripts
|
||||||
|
* @param source - Plugin function, script URL, or array of URLs
|
||||||
|
* @returns SigPro instance or Promise
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Load external library
|
||||||
|
* await $.plugin('https://cdn.jsdelivr.net/npm/lodash/lodash.min.js');
|
||||||
|
*
|
||||||
|
* // Register plugin
|
||||||
|
* $.plugin(($) => {
|
||||||
|
* $.customMethod = () => console.log('Custom method');
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // Load multiple scripts
|
||||||
|
* await $.plugin(['lib1.js', 'lib2.js']);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
plugin: Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a reactive HTML element
|
||||||
|
* @param tag - HTML tag name
|
||||||
|
* @param props - Attributes and event handlers
|
||||||
|
* @param content - Child content
|
||||||
|
* @returns Live DOM element
|
||||||
|
*/
|
||||||
|
function html(tag: string, props?: HtmlProps | HtmlContent, content?: HtmlContent): HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mount a component to the DOM
|
||||||
|
* @param node - Component or element to mount
|
||||||
|
* @param target - Target element or CSS selector
|
||||||
|
*/
|
||||||
|
function mount(node: HTMLElement | (() => HTMLElement), target?: HTMLElement | string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type-safe HTML element creators for common tags
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Using tag functions directly
|
||||||
|
* const myDiv = div({ class: 'container' }, [
|
||||||
|
* h1("Title"),
|
||||||
|
* p("Paragraph text"),
|
||||||
|
* button({ onclick: () => alert('Clicked!') }, "Click me")
|
||||||
|
* ]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
interface HtmlTagCreator {
|
||||||
|
/**
|
||||||
|
* Create HTML element with props and content
|
||||||
|
* @param props - HTML attributes and event handlers
|
||||||
|
* @param content - Child nodes or content
|
||||||
|
* @returns HTMLElement
|
||||||
|
*/
|
||||||
|
(props?: HtmlProps | HtmlContent, content?: HtmlContent): HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-safe tag creators
|
||||||
|
const div: HtmlTagCreator;
|
||||||
|
const span: HtmlTagCreator;
|
||||||
|
const p: HtmlTagCreator;
|
||||||
|
const button: HtmlTagCreator;
|
||||||
|
const h1: HtmlTagCreator;
|
||||||
|
const h2: HtmlTagCreator;
|
||||||
|
const h3: HtmlTagCreator;
|
||||||
|
const ul: HtmlTagCreator;
|
||||||
|
const ol: HtmlTagCreator;
|
||||||
|
const li: HtmlTagCreator;
|
||||||
|
const a: HtmlTagCreator;
|
||||||
|
const label: HtmlTagCreator;
|
||||||
|
const section: HtmlTagCreator;
|
||||||
|
const nav: HtmlTagCreator;
|
||||||
|
const main: HtmlTagCreator;
|
||||||
|
const header: HtmlTagCreator;
|
||||||
|
const footer: HtmlTagCreator;
|
||||||
|
const input: HtmlTagCreator;
|
||||||
|
const form: HtmlTagCreator;
|
||||||
|
const img: HtmlTagCreator;
|
||||||
|
const select: HtmlTagCreator;
|
||||||
|
const option: HtmlTagCreator;
|
||||||
|
const table: HtmlTagCreator;
|
||||||
|
const thead: HtmlTagCreator;
|
||||||
|
const tbody: HtmlTagCreator;
|
||||||
|
const tr: HtmlTagCreator;
|
||||||
|
const th: HtmlTagCreator;
|
||||||
|
const td: HtmlTagCreator;
|
||||||
|
const canvas: HtmlTagCreator;
|
||||||
|
const video: HtmlTagCreator;
|
||||||
|
const audio: HtmlTagCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper types for common use cases
|
||||||
|
*/
|
||||||
|
export namespace SigProTypes {
|
||||||
|
/**
|
||||||
|
* Extract the value type from a Signal
|
||||||
|
*/
|
||||||
|
type SignalValue<T> = T extends Signal<infer U> ? U : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the return type from a Computed
|
||||||
|
*/
|
||||||
|
type ComputedValue<T> = T extends Computed<infer U> ? U : never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props for a component function
|
||||||
|
*/
|
||||||
|
interface ComponentProps {
|
||||||
|
children?: HtmlContent;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component function type
|
||||||
|
*/
|
||||||
|
type Component<P extends ComponentProps = ComponentProps> = (props: P) => HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async component type (for lazy loading)
|
||||||
|
*/
|
||||||
|
type AsyncComponent = () => Promise<{ default: Component }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
|
|
||||||
|
// Make sure $ is available globally
|
||||||
|
declare const $: SigPro;
|
||||||
@@ -2,27 +2,18 @@
|
|||||||
* SigPro - Atomic Unified Reactive Engine
|
* SigPro - Atomic Unified Reactive Engine
|
||||||
* A lightweight, fine-grained reactivity system with built-in routing and plugin support.
|
* A lightweight, fine-grained reactivity system with built-in routing and plugin support.
|
||||||
* @author Gemini & User
|
* @author Gemini & User
|
||||||
|
*
|
||||||
|
* Type definitions available in sigpro.d.ts
|
||||||
*/
|
*/
|
||||||
(() => {
|
(() => {
|
||||||
/** @type {Function|null} Internal tracker for the currently executing reactive effect. */
|
/** @type {Function|null} Internal tracker for the currently executing reactive effect. */
|
||||||
let activeEffect = null;
|
let activeEffect = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} SigPro
|
* Creates a reactive Signal or Computed value
|
||||||
* @property {function(any|function, string=): Function} $ - Creates a Signal or Computed. Optional key for localStorage.
|
* @param {any|Function} initial - Initial value or computed function
|
||||||
* @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element.
|
* @param {string} [key] - Optional localStorage key for persistence
|
||||||
* @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM.
|
* @returns {Function} Reactive accessor/mutator function
|
||||||
* @property {function(Array<Object>): HTMLElement} router - Initializes a hash-based router.
|
|
||||||
* @property {function(string): void} router.go - Programmatic navigation to a hash path.
|
|
||||||
* @property {function((function|string|Array<string>)): (Promise<SigPro>|SigPro)} plugin - Extends SigPro or loads external scripts.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Signal (state) or a Computed/Effect (reaction).
|
|
||||||
* Supports optional persistence in localStorage.
|
|
||||||
* * @param {any|function} initial - Initial value or a function for computed logic.
|
|
||||||
* @param {string} [key] - Optional localStorage key for automatic state persistence.
|
|
||||||
* @returns {Function} A reactive accessor/mutator function.
|
|
||||||
*/
|
*/
|
||||||
const $ = (initial, key) => {
|
const $ = (initial, key) => {
|
||||||
const subs = new Set();
|
const subs = new Set();
|
||||||
@@ -69,11 +60,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hyperscript engine to render reactive HTML nodes.
|
* Creates reactive HTML elements
|
||||||
* @param {string} tag - The HTML tag name (e.g., 'div', 'button').
|
* @param {string} tag - HTML tag name
|
||||||
* @param {Object} [props] - Attributes, events (onclick), or reactive props ($value, $class).
|
* @param {Object} [props] - Attributes and event handlers
|
||||||
* @param {any} [content] - String, Node, Array of nodes, or reactive function.
|
* @param {any} [content] - Child content
|
||||||
* @returns {HTMLElement} A live DOM element linked to SigPro signals.
|
* @returns {HTMLElement}
|
||||||
*/
|
*/
|
||||||
$.html = (tag, props = {}, content = []) => {
|
$.html = (tag, props = {}, content = []) => {
|
||||||
const el = document.createElement(tag);
|
const el = document.createElement(tag);
|
||||||
@@ -87,12 +78,10 @@
|
|||||||
el.addEventListener(key.toLowerCase().slice(2), val);
|
el.addEventListener(key.toLowerCase().slice(2), val);
|
||||||
} else if (key.startsWith('$')) {
|
} else if (key.startsWith('$')) {
|
||||||
const attr = key.slice(1);
|
const attr = key.slice(1);
|
||||||
// Two-way binding for inputs
|
|
||||||
if ((attr === 'value' || attr === 'checked') && typeof val === 'function') {
|
if ((attr === 'value' || attr === 'checked') && typeof val === 'function') {
|
||||||
const ev = attr === 'checked' ? 'change' : 'input';
|
const ev = attr === 'checked' ? 'change' : 'input';
|
||||||
el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value));
|
el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value));
|
||||||
}
|
}
|
||||||
// Reactive attribute update
|
|
||||||
$(() => {
|
$(() => {
|
||||||
const v = typeof val === 'function' ? val() : val;
|
const v = typeof val === 'function' ? val() : val;
|
||||||
if (attr === 'value' || attr === 'checked') el[attr] = v;
|
if (attr === 'value' || attr === 'checked') el[attr] = v;
|
||||||
@@ -126,9 +115,9 @@
|
|||||||
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
|
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application mounter.
|
* Mounts a component to the DOM
|
||||||
* @param {HTMLElement|function} node - Root component or element to mount.
|
* @param {HTMLElement|Function} node - Component or element to mount
|
||||||
* @param {HTMLElement|string} [target=document.body] - Target element or CSS selector.
|
* @param {HTMLElement|string} [target=document.body] - Target element or selector
|
||||||
*/
|
*/
|
||||||
$.mount = (node, target = document.body) => {
|
$.mount = (node, target = document.body) => {
|
||||||
const el = typeof target === 'string' ? document.querySelector(target) : target;
|
const el = typeof target === 'string' ? document.querySelector(target) : target;
|
||||||
@@ -139,10 +128,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a reactive hash-based router.
|
* Initializes a hash-based router
|
||||||
* Maps URL hash changes to component rendering and supports Vite's dynamic imports.
|
* @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes
|
||||||
* * @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes - Array of route objects.
|
* @returns {HTMLElement}
|
||||||
* @returns {HTMLElement} A reactive div container that swaps content based on the current hash.
|
|
||||||
*/
|
*/
|
||||||
$.router = (routes) => {
|
$.router = (routes) => {
|
||||||
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
|
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
|
||||||
@@ -185,20 +173,17 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Programmatically navigates to a specific path using the hash.
|
* Programmatic navigation
|
||||||
* * @param {string} path - The destination path (e.g., '/home' or 'settings').
|
* @param {string} path - Destination path
|
||||||
* @example
|
|
||||||
* $.router.go('/profile/42');
|
|
||||||
*/
|
*/
|
||||||
$.router.go = (path) => {
|
$.router.go = (path) => {
|
||||||
window.location.hash = path.startsWith('/') ? path : `/${path}`;
|
window.location.hash = path.startsWith('/') ? path : `/${path}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Polymorphic Plugin System.
|
* Plugin system - extends SigPro or loads external scripts
|
||||||
* Registers internal functions or loads external .js files as plugins.
|
* @param {Function|string|string[]} source - Plugin or script URL(s)
|
||||||
* @param {function|string|Array<string>} source - Plugin function or URL(s).
|
* @returns {Promise<SigPro>|SigPro}
|
||||||
* @returns {Promise<SigPro>|SigPro} Resolves with the $ instance after loading or registering.
|
|
||||||
*/
|
*/
|
||||||
$.plugin = (source) => {
|
$.plugin = (source) => {
|
||||||
if (typeof source === 'function') {
|
if (typeof source === 'function') {
|
||||||
|
|||||||
261
vite/index.d.ts
vendored
Normal file
261
vite/index.d.ts
vendored
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
/**
|
||||||
|
* SigPro Vite Plugin
|
||||||
|
* Provides file-based routing and development tools for Vite
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* // vite.config.js
|
||||||
|
* import { defineConfig } from 'vite';
|
||||||
|
* import sigproRouter from 'sigpro/vite';
|
||||||
|
*
|
||||||
|
* export default defineConfig({
|
||||||
|
* plugins: [sigproRouter()]
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Plugin } from 'vite';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File-based routing configuration options
|
||||||
|
*/
|
||||||
|
export interface SigProRouterOptions {
|
||||||
|
/**
|
||||||
|
* Directory where page components are stored
|
||||||
|
* @default 'src/pages'
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* sigproRouter({ pagesDir: 'src/views' })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
pagesDir?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File extensions to consider as pages
|
||||||
|
* @default ['.js', '.jsx', '.ts', '.tsx']
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* sigproRouter({ extensions: ['.js', '.jsx'] })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
extensions?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom route transformation function
|
||||||
|
* @param filePath - Absolute path to the file
|
||||||
|
* @returns Custom route path or null to skip
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* sigproRouter({
|
||||||
|
* transformRoute: (path) => {
|
||||||
|
* // Convert 'src/pages/blog/post.js' to '/blog/post'
|
||||||
|
* return path.replace(/^src\/pages/, '').replace(/\.js$/, '');
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
transformRoute?: (filePath: string) => string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base path prefix for all routes
|
||||||
|
* @default ''
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* sigproRouter({ basePath: '/app' }) // Routes become /app/about, /app/users, etc.
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
basePath?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom 404 component path
|
||||||
|
* @default null (uses default "404 - Not Found")
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* sigproRouter({ notFoundComponent: 'src/pages/NotFound.js' })
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
notFoundComponent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route configuration generated by the plugin
|
||||||
|
*/
|
||||||
|
export interface GeneratedRoute {
|
||||||
|
/** URL path pattern (supports :params and * wildcard) */
|
||||||
|
path: string;
|
||||||
|
/** Lazy-loaded component */
|
||||||
|
component: () => Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual module ID for generated routes
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* import { routes } from 'virtual:sigpro-routes';
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const virtualModuleId = 'virtual:sigpro-routes';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin for SigPro file-based routing
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - 📁 File-based routing from `src/pages` directory
|
||||||
|
* - 🔄 Automatic route generation
|
||||||
|
* - 🚀 Lazy-loading support with code splitting
|
||||||
|
* - 📦 Dynamic routes with `[param]` syntax → `/:param`
|
||||||
|
* - ⭐ Catch-all routes with `[...param]` syntax → `/*`
|
||||||
|
* - 🎯 Index routes (`index.js` becomes `/`)
|
||||||
|
* - 🧹 Automatic sorting (static routes first, then dynamic)
|
||||||
|
* - 🎨 404 fallback generation
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* **File Structure:**
|
||||||
|
* ```
|
||||||
|
* src/pages/
|
||||||
|
* ├── index.js → '/'
|
||||||
|
* ├── about.js → '/about'
|
||||||
|
* ├── blog/
|
||||||
|
* │ ├── index.js → '/blog'
|
||||||
|
* │ ├── [slug].js → '/blog/:slug'
|
||||||
|
* │ └── [...catch].js → '/blog/*'
|
||||||
|
* └── user/
|
||||||
|
* ├── index.js → '/user'
|
||||||
|
* └── [id].js → '/user/:id'
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* **Generated Routes:**
|
||||||
|
* ```javascript
|
||||||
|
* // virtual:sigpro-routes
|
||||||
|
* export const routes = [
|
||||||
|
* { path: '/', component: () => import('src/pages/index.js') },
|
||||||
|
* { path: '/about', component: () => import('src/pages/about.js') },
|
||||||
|
* { path: '/blog', component: () => import('src/pages/blog/index.js') },
|
||||||
|
* { path: '/blog/:slug', component: () => import('src/pages/blog/[slug].js') },
|
||||||
|
* { path: '/blog/*', component: () => import('src/pages/blog/[...catch].js') },
|
||||||
|
* { path: '/user', component: () => import('src/pages/user/index.js') },
|
||||||
|
* { path: '/user/:id', component: () => import('src/pages/user/[id].js') },
|
||||||
|
* { path: '*', component: () => import('src/pages/NotFound.js') } // If exists
|
||||||
|
* ];
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* **Usage with SigPro:**
|
||||||
|
* ```javascript
|
||||||
|
* // main.js
|
||||||
|
* import { routes } from 'virtual:sigpro-routes';
|
||||||
|
* import { $ } from 'sigpro';
|
||||||
|
*
|
||||||
|
* const router = $.router(routes);
|
||||||
|
* $.mount(router, '#app');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param options - Plugin configuration options
|
||||||
|
* @returns Vite plugin object
|
||||||
|
*/
|
||||||
|
export default function sigproRouter(options?: SigProRouterOptions): Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page component props for route components
|
||||||
|
* @template Params - Type of route parameters
|
||||||
|
*/
|
||||||
|
export interface PageProps<Params = Record<string, string>> {
|
||||||
|
/** URL parameters from dynamic routes (e.g., :id, :slug) */
|
||||||
|
params: Params;
|
||||||
|
/** URL search parameters (query string) */
|
||||||
|
query: URLSearchParams;
|
||||||
|
/** Current pathname without query string */
|
||||||
|
pathname: string;
|
||||||
|
/** Children components (for nested routes) */
|
||||||
|
children?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route component type
|
||||||
|
* @template Params - Type of route parameters
|
||||||
|
*/
|
||||||
|
export type PageComponent<Params = Record<string, string>> = (
|
||||||
|
props: PageProps<Params>
|
||||||
|
) => HTMLElement | Promise<HTMLElement>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create typed route components
|
||||||
|
* Provides better TypeScript support and autocompletion
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* // pages/user/[id].js
|
||||||
|
* import { createPage } from 'sigpro/vite';
|
||||||
|
*
|
||||||
|
* export default createPage(({ params, query }) => {
|
||||||
|
* const { id } = params; // TypeScript knows this is a string
|
||||||
|
* const page = query.get('page') || 1;
|
||||||
|
*
|
||||||
|
* return div({ class: 'user-profile' }, [
|
||||||
|
* h1(`User ${id}`),
|
||||||
|
* p(`Page: ${page}`),
|
||||||
|
* button({
|
||||||
|
* onclick: () => console.log('Viewing user', id)
|
||||||
|
* }, 'View Profile')
|
||||||
|
* ]);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* **With TypeScript:**
|
||||||
|
* ```typescript
|
||||||
|
* // pages/user/[id].tsx
|
||||||
|
* interface UserParams {
|
||||||
|
* id: string;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* export default createPage<UserParams>(({ params, query }) => {
|
||||||
|
* const userId = params.id; // TypeScript knows this is string
|
||||||
|
* // ... rest of component
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function createPage<Params = Record<string, string>>(
|
||||||
|
component: (props: PageProps<Params>) => HTMLElement | Promise<HTMLElement>
|
||||||
|
): PageComponent<Params>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard to check if a component is lazy-loaded
|
||||||
|
* @param component - Component to check
|
||||||
|
* @returns True if component is a lazy-loaded module
|
||||||
|
*/
|
||||||
|
export function isLazyComponent(component: any): component is () => Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to preload routes for better performance
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* import { preloadRoutes } from 'sigpro/vite';
|
||||||
|
*
|
||||||
|
* // Preload routes after initial load
|
||||||
|
* preloadRoutes(['/about', '/blog']);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function preloadRoutes(paths: string[]): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug helper to inspect generated routes
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* // Only in development
|
||||||
|
* if (import.meta.env.DEV) {
|
||||||
|
* const routes = await import('virtual:sigpro-routes');
|
||||||
|
* console.table(routes);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function inspectRoutes(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual module declarations for client-side code
|
||||||
|
* This allows TypeScript to understand the virtual module
|
||||||
|
*/
|
||||||
|
declare module 'virtual:sigpro-routes' {
|
||||||
|
export const routes: GeneratedRoute[];
|
||||||
|
}
|
||||||
@@ -1,4 +1,28 @@
|
|||||||
// src/vite/index.js
|
/**
|
||||||
import sigproRouterPlugin from './sigproRouter.js';
|
* SigPro Vite Plugin
|
||||||
export const sigproRouter = sigproRouterPlugin;
|
* @module sigpro/vite
|
||||||
export default sigproRouterPlugin;
|
*/
|
||||||
|
|
||||||
|
// ✅ Correcto: importas la función y la llamas como quieras
|
||||||
|
import sigproRouter from './router.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vite plugin for SigPro file-based routing
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* // vite.config.js
|
||||||
|
* import sigproRouter from 'sigpro/vite';
|
||||||
|
*
|
||||||
|
* export default {
|
||||||
|
* plugins: [sigproRouter()]
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {import('./index.d.ts').SigProRouterOptions} [options] - Plugin configuration options
|
||||||
|
* @returns {import('vite').Plugin} Vite plugin instance
|
||||||
|
*/
|
||||||
|
export default sigproRouter;
|
||||||
|
|
||||||
|
// Opcional: también exportar como named export
|
||||||
|
export { sigproRouter };
|
||||||
Reference in New Issue
Block a user