diff --git a/package.json b/package.json index 664a15e..03e7b76 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,34 @@ "type": "module", "license": "MIT", "exports": { - ".": "./index.js", - "./plugins": "./plugins/index.js", - "./vite/*": "./vite/*.js" + ".": { + "import": "./index.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/", "repository": { "type": "git", diff --git a/plugins/index.d.ts b/plugins/index.d.ts new file mode 100644 index 0000000..2e6a0da --- /dev/null +++ b/plugins/index.d.ts @@ -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; +} + +/** + * 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; + /** Timeout in milliseconds */ + timeout?: number; + } + + interface DebugOptions { + /** Enable verbose logging */ + verbose?: boolean; + /** Log to console */ + logToConsole?: boolean; + /** Enable time travel debugging */ + timeTravel?: boolean; + } +} \ No newline at end of file diff --git a/plugins/index.js b/plugins/index.js index 1faf31f..06fd422 100644 --- a/plugins/index.js +++ b/plugins/index.js @@ -1,6 +1,4 @@ // /plugins/index.js export { UI } from './ui.js'; export { Fetch } from './fetch.js'; -export { Storage } from './storage.js'; export { Debug } from './debug.js'; -export { Router } from './router.js'; diff --git a/plugins/router.js b/plugins/router.js deleted file mode 100644 index ba8409f..0000000 --- a/plugins/router.js +++ /dev/null @@ -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; -}; \ No newline at end of file diff --git a/plugins/storage.js b/plugins/storage.js deleted file mode 100644 index 98a9894..0000000 --- a/plugins/storage.js +++ /dev/null @@ -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; - }; -}; \ No newline at end of file diff --git a/sigpro/sigpro.d.ts b/sigpro/sigpro.d.ts new file mode 100644 index 0000000..5f343f8 --- /dev/null +++ b/sigpro/sigpro.d.ts @@ -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 { + /** 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 { + /** 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 { + /** Two-way binding for input values */ + $value?: Signal | ((val: any) => void); + /** Two-way binding for checkbox/radio checked state */ + $checked?: Signal | ((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) => HTMLElement | Promise) | Promise | 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; + } + + /** + * 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())); + * ``` + */ + (initial: T, key?: string): Signal; + (computed: () => T, key?: string): Computed; + + /** + * 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 extends Signal ? U : never; + + /** + * Extract the return type from a Computed + */ + type ComputedValue = T extends Computed ? U : never; + + /** + * Props for a component function + */ + interface ComponentProps { + children?: HtmlContent; + [key: string]: any; + } + + /** + * Component function type + */ + type Component

= (props: P) => HTMLElement; + + /** + * Async component type (for lazy loading) + */ + type AsyncComponent = () => Promise<{ default: Component }>; +} + +export {}; + +// Make sure $ is available globally +declare const $: SigPro; \ No newline at end of file diff --git a/sigpro/sigpro.js b/sigpro/sigpro.js index 995ed40..bc2c1ae 100644 --- a/sigpro/sigpro.js +++ b/sigpro/sigpro.js @@ -2,28 +2,19 @@ * SigPro - Atomic Unified Reactive Engine * A lightweight, fine-grained reactivity system with built-in routing and plugin support. * @author Gemini & User + * + * Type definitions available in sigpro.d.ts */ (() => { /** @type {Function|null} Internal tracker for the currently executing reactive effect. */ let activeEffect = null; /** - * @typedef {Object} SigPro - * @property {function(any|function, string=): Function} $ - Creates a Signal or Computed. Optional key for localStorage. - * @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element. - * @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM. - * @property {function(Array): HTMLElement} router - Initializes a hash-based router. - * @property {function(string): void} router.go - Programmatic navigation to a hash path. - * @property {function((function|string|Array)): (Promise|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. - */ + * Creates a reactive Signal or Computed value + * @param {any|Function} initial - Initial value or computed function + * @param {string} [key] - Optional localStorage key for persistence + * @returns {Function} Reactive accessor/mutator function + */ const $ = (initial, key) => { const subs = new Set(); @@ -69,11 +60,11 @@ }; /** - * Hyperscript engine to render reactive HTML nodes. - * @param {string} tag - The HTML tag name (e.g., 'div', 'button'). - * @param {Object} [props] - Attributes, events (onclick), or reactive props ($value, $class). - * @param {any} [content] - String, Node, Array of nodes, or reactive function. - * @returns {HTMLElement} A live DOM element linked to SigPro signals. + * Creates reactive HTML elements + * @param {string} tag - HTML tag name + * @param {Object} [props] - Attributes and event handlers + * @param {any} [content] - Child content + * @returns {HTMLElement} */ $.html = (tag, props = {}, content = []) => { const el = document.createElement(tag); @@ -87,12 +78,10 @@ el.addEventListener(key.toLowerCase().slice(2), val); } else if (key.startsWith('$')) { const attr = key.slice(1); - // Two-way binding for inputs if ((attr === 'value' || attr === 'checked') && typeof val === 'function') { const ev = attr === 'checked' ? 'change' : 'input'; el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value)); } - // Reactive attribute update $(() => { const v = typeof val === 'function' ? val() : val; if (attr === 'value' || attr === 'checked') el[attr] = v; @@ -126,9 +115,9 @@ tags.forEach(t => window[t] = (p, c) => $.html(t, p, c)); /** - * Application mounter. - * @param {HTMLElement|function} node - Root component or element to mount. - * @param {HTMLElement|string} [target=document.body] - Target element or CSS selector. + * Mounts a component to the DOM + * @param {HTMLElement|Function} node - Component or element to mount + * @param {HTMLElement|string} [target=document.body] - Target element or selector */ $.mount = (node, target = document.body) => { const el = typeof target === 'string' ? document.querySelector(target) : target; @@ -139,11 +128,10 @@ }; /** - * Initializes a reactive 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 - Array of route objects. - * @returns {HTMLElement} A reactive div container that swaps content based on the current hash. - */ + * Initializes a hash-based router + * @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes + * @returns {HTMLElement} + */ $.router = (routes) => { const sPath = $(window.location.hash.replace(/^#/, "") || "/"); window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/")); @@ -185,20 +173,17 @@ }; /** - * Programmatically navigates to a specific path using the hash. - * * @param {string} path - The destination path (e.g., '/home' or 'settings'). - * @example - * $.router.go('/profile/42'); + * Programmatic navigation + * @param {string} path - Destination path */ $.router.go = (path) => { window.location.hash = path.startsWith('/') ? path : `/${path}`; }; /** - * Polymorphic Plugin System. - * Registers internal functions or loads external .js files as plugins. - * @param {function|string|Array} source - Plugin function or URL(s). - * @returns {Promise|SigPro} Resolves with the $ instance after loading or registering. + * Plugin system - extends SigPro or loads external scripts + * @param {Function|string|string[]} source - Plugin or script URL(s) + * @returns {Promise|SigPro} */ $.plugin = (source) => { if (typeof source === 'function') { diff --git a/vite/index.d.ts b/vite/index.d.ts new file mode 100644 index 0000000..66cb533 --- /dev/null +++ b/vite/index.d.ts @@ -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; +} + +/** + * 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> { + /** 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> = ( + props: PageProps +) => HTMLElement | Promise; + +/** + * 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(({ params, query }) => { + * const userId = params.id; // TypeScript knows this is string + * // ... rest of component + * }); + * ``` + */ +export function createPage>( + component: (props: PageProps) => HTMLElement | Promise +): PageComponent; + +/** + * 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; + +/** + * 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; + +/** + * 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[]; +} \ No newline at end of file diff --git a/vite/index.js b/vite/index.js index b7efde7..05d4795 100644 --- a/vite/index.js +++ b/vite/index.js @@ -1,4 +1,28 @@ -// src/vite/index.js -import sigproRouterPlugin from './sigproRouter.js'; -export const sigproRouter = sigproRouterPlugin; -export default sigproRouterPlugin; \ No newline at end of file +/** + * SigPro Vite Plugin + * @module sigpro/vite + */ + +// βœ… 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 }; \ No newline at end of file