@lytjs/renderer 6.5.0 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +385 -385
- package/dist/index.cjs +5 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +5 -20
- package/dist/index.mjs.map +1 -1
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.mjs.map +1 -1
- package/dist/vapor/vapor-app.cjs.map +1 -1
- package/dist/vapor/vapor-app.mjs.map +1 -1
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/signal/signal-renderer.ts","../../src/vapor/vapor-hmr.ts","../../src/vapor/vapor-app.ts"],"names":["compile"],"mappings":"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","file":"vapor-app.mjs","sourcesContent":["// src/signal/signal-renderer.ts\r\n// @lytjs/renderer - Signal 模式渲染器\r\n// 使用 @lytjs/compiler 编译模板为 Signal 模式代码,\r\n// 通过 @lytjs/dom-runtime 提供的细粒度 DOM 操作函数执行渲染\r\n\r\nimport { compile, clearCompileCache } from '@lytjs/compiler';\r\nimport { effect } from '@lytjs/reactivity';\r\nimport {\r\n insert,\r\n remove,\r\n runCleanups,\r\n onCleanup,\r\n createTemplate,\r\n setText,\r\n setHTML,\r\n setAttribute,\r\n setProperty,\r\n setStyle,\r\n setClass,\r\n createEventHandler,\r\n reconcileArray,\r\n} from '@lytjs/dom-runtime';\r\n\r\n// ============================================================\r\n// SignalRenderer 接口\r\n// ============================================================\r\n\r\nexport interface SignalRenderer {\r\n /** 将模板渲染到指定的容器元素或 CSS 选择器 */\r\n render(container: Element | string): void;\r\n /** 卸载渲染器,清理所有 effect 和 DOM */\r\n unmount(): void;\r\n}\r\n\r\n// ============================================================\r\n// createSignalRenderer 工厂函数\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个 Signal 模式的渲染器\r\n *\r\n * @param template - 模板字符串\r\n * @param context - 模板上下文(响应式数据)\r\n * @returns SignalRenderer 实例\r\n *\r\n * @example\r\n * ```ts\r\n * import { ref } from '@lytjs/reactivity';\r\n * import { createSignalRenderer } from '@lytjs/renderer';\r\n *\r\n * const ctx = { message: ref('hello') };\r\n * const renderer = createSignalRenderer('<div>{{ message }}</div>', ctx);\r\n * renderer.render('#app');\r\n * ```\r\n */\r\nexport function createSignalRenderer(\r\n template: string,\r\n context: Record<string, unknown>,\r\n): SignalRenderer {\r\n let cleanup: (() => void) | null = null;\r\n\r\n // 编译模板为 Signal 模式(缓存编译结果,避免每次 render 重新编译)\r\n let code: string;\r\n let renderBody: string | null;\r\n try {\r\n // 清除缓存,确保使用最新的 codegen\r\n clearCompileCache();\r\n const compileResult = compile(template, { rendererMode: 'signal', optimizeSignal: false });\r\n code = compileResult.code;\r\n\r\n // 从编译结果中提取 render 函数体\r\n // codegen-signal 生成的代码结构:\r\n // import { effect, reconcileArray } from '@lytjs/reactivity';\r\n // import { createTemplate, ... } from '@lytjs/dom-runtime';\r\n // export function render(_ctx, _container) { ... }\r\n // return () => { runCleanups(); };\r\n //\r\n // 我们需要提取 render 函数体,并通过 new Function 执行\r\n renderBody = extractRenderBody(code);\r\n if (!renderBody) {\r\n throw new Error(\r\n `[LytJS] SignalRenderer: failed to extract render function from compiled code.`,\r\n );\r\n }\r\n } catch (e) {\r\n throw e instanceof Error\r\n ? new Error(`[LytJS] SignalRenderer: template compilation failed. ${e.message}`)\r\n : new Error(`[LytJS] SignalRenderer: template compilation failed. ${String(e)}`);\r\n }\r\n\r\n return {\r\n render(container: Element | string) {\r\n // 卸载旧的渲染\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = null;\r\n }\r\n\r\n // FIX: P2-32 移除非空断言,添加 null 检查\r\n const el = typeof container === 'string' ? document.querySelector(container) : container;\r\n\r\n if (!el) {\r\n throw new Error(`[LytJS] SignalRenderer: cannot find element matching \"${container}\".`);\r\n }\r\n\r\n try {\r\n // FIX: P1-15 添加安全警告注释\r\n // 注意:此处使用 new Function() 执行编译后的模板代码。\r\n // 虽然模板代码由编译器生成(而非用户直接输入),但仍存在潜在的安全风险。\r\n // 建议在生产环境中使用预编译(AOT compilation)替代运行时编译。\r\n // [P2-batch2-3] 已确认安全风险并记录。当前实现依赖编译器可信输入,\r\n // 后续版本应考虑使用 AOT 编译或沙箱执行环境来消除此风险。\r\n // 生产环境建议使用 AOT 预编译替代运行时编译\r\n // 创建渲染函数,传入所有 dom-runtime 和 reactivity 的函数作为参数\r\n // 参数名必须与 codegen-signal.ts 生成的 import 名称一致\r\n // FIX: P0-2 使用 setSafeHTML 替代 setHTML,避免 XSS 攻击\r\n // 注意:生成的代码使用短别名和 _c/_n 参数名\r\n // 参数顺序:effect, reconcileArray, createTemplate, setText, setHTML, setAttribute,\r\n // setProperty, setStyle, setClass, insert, remove, createEventHandler,\r\n // bindEffect, onCleanup, runCleanups, ctx, container\r\n const renderFn = new Function(\r\n 'effect',\r\n 'reconcileArray',\r\n 'createTemplate',\r\n 'setText',\r\n 'setHTML',\r\n 'setAttribute',\r\n 'setProperty',\r\n 'setStyle',\r\n 'setClass',\r\n 'insert',\r\n 'remove',\r\n 'createEventHandler',\r\n 'onCleanup',\r\n 'runCleanups',\r\n '_ctx',\r\n '_container',\r\n renderBody,\r\n );\r\n\r\n // 执行渲染函数\r\n // 给ctx加proxy,解包ref\r\n const proxiedCtx = new Proxy<Record<string, unknown>>(context as Record<string, unknown>, {\r\n get(target, prop) {\r\n const val = (target as Record<string, unknown>)[prop as string];\r\n if (val && typeof val === 'object' && 'value' in val) {\r\n return (val as { value: unknown }).value;\r\n }\r\n return val;\r\n },\r\n set(target, prop, value) {\r\n const val = (target as Record<string, unknown>)[prop as string];\r\n if (val && typeof val === 'object' && 'value' in val) {\r\n (val as { value: unknown }).value = value;\r\n return true;\r\n }\r\n (target as Record<string, unknown>)[prop as string] = value;\r\n return true;\r\n },\r\n });\r\n const cleanupFn = renderFn(\r\n effect,\r\n reconcileArray,\r\n createTemplate,\r\n setText,\r\n setHTML,\r\n setAttribute,\r\n setProperty,\r\n setStyle,\r\n setClass,\r\n insert,\r\n remove,\r\n createEventHandler,\r\n onCleanup,\r\n runCleanups,\r\n proxiedCtx,\r\n el,\r\n );\r\n\r\n // 保存清理函数\r\n if (typeof cleanupFn === 'function') {\r\n cleanup = cleanupFn;\r\n }\r\n } catch (e) {\r\n throw e instanceof Error\r\n ? new Error(`[LytJS] SignalRenderer: render execution failed. ${e.message}`)\r\n : new Error(`[LytJS] SignalRenderer: render execution failed. ${String(e)}`);\r\n }\r\n },\r\n\r\n unmount() {\r\n if (cleanup) {\r\n cleanup();\r\n cleanup = null;\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ============================================================\r\n// 辅助函数:从编译代码中提取 render 函数体\r\n// ============================================================\r\n\r\n/**\r\n * 从 codegen-signal 生成的代码中提取 render 函数体\r\n *\r\n * 生成的代码结构:\r\n * ```\r\n * import { effect, reconcileArray } from '@lytjs/reactivity';\r\n * import { createTemplate, ... } from '@lytjs/dom-runtime';\r\n *\r\n * export function render(_ctx, _container) {\r\n * ...\r\n * return () => { runCleanups(); };\r\n * }\r\n * ```\r\n *\r\n * 我们需要提取函数体(花括号内的内容),去掉 import 语句和函数声明\r\n *\r\n * FIX: P2-33 边界情况说明:\r\n * - 本函数假设输入代码是由 codegen-signal 生成的标准格式\r\n * - 不支持嵌套函数声明或复杂的花括号嵌套(如对象字面量中的方法)\r\n * - 字符串和注释中的花括号会被正确跳过\r\n * - 如果代码结构不符合预期,可能返回 null 或不完整的结果\r\n */\r\nfunction extractRenderBody(code: string): string | null {\r\n // 匹配 render 函数体\r\n // 查找 \"export function render(...) {\" 和对应的闭合 \"}\"\r\n // 支持不同的参数名:_ctx/_container, _c/_n 等\r\n const funcMatch = code.match(/export\\s+function\\s+render\\s*\\([^)]*\\)\\s*\\{/);\r\n\r\n if (!funcMatch) {\r\n return null;\r\n }\r\n\r\n const startIndex = funcMatch.index! + funcMatch[0]!.length;\r\n\r\n // 找到匹配的闭合花括号,跳过字符串和注释中的花括号\r\n let depth = 1;\r\n let i = startIndex;\r\n while (i < code.length && depth > 0) {\r\n const ch = code[i]!;\r\n\r\n // 跳过单引号字符串\r\n if (ch === \"'\") {\r\n i++;\r\n while (i < code.length && code[i] !== \"'\") {\r\n if (code[i] === '\\\\') i++; // 跳过转义字符\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n // 跳过双引号字符串\r\n if (ch === '\"') {\r\n i++;\r\n while (i < code.length && code[i] !== '\"') {\r\n if (code[i] === '\\\\') i++; // 跳过转义字符\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n // 跳过模板字符串\r\n if (ch === '`') {\r\n i++;\r\n while (i < code.length && code[i] !== '`') {\r\n if (code[i] === '\\\\') i++; // 跳过转义字符\r\n if (code[i] === '$' && code[i + 1] === '{') {\r\n // FIX: P0-8 模板字符串中的 ${} 表达式,使用子循环跳过,\r\n // 不修改外层 depth,避免 depth 泄漏导致提前闭合\r\n i += 2;\r\n let exprDepth = 1;\r\n while (i < code.length && exprDepth > 0) {\r\n // 跳过表达式内的字符串\r\n if (code[i] === \"'\") {\r\n i++;\r\n while (i < code.length && code[i] !== \"'\") {\r\n if (code[i] === '\\\\') i++;\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (code[i] === '\"') {\r\n i++;\r\n while (i < code.length && code[i] !== '\"') {\r\n if (code[i] === '\\\\') i++;\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (code[i] === '`') {\r\n i++;\r\n while (i < code.length && code[i] !== '`') {\r\n if (code[i] === '\\\\') i++;\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n if (code[i] === '{') exprDepth++;\r\n if (code[i] === '}') exprDepth--;\r\n i++;\r\n }\r\n continue;\r\n }\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n // 跳过单行注释\r\n if (ch === '/' && code[i + 1] === '/') {\r\n i += 2;\r\n while (i < code.length && code[i] !== '\\n') {\r\n i++;\r\n }\r\n i++;\r\n continue;\r\n }\r\n\r\n // 跳过多行注释\r\n if (ch === '/' && code[i + 1] === '*') {\r\n i += 2;\r\n while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) {\r\n i++;\r\n }\r\n i += 2;\r\n continue;\r\n }\r\n\r\n if (ch === '{') {\r\n depth++;\r\n } else if (ch === '}') {\r\n depth--;\r\n }\r\n i++;\r\n }\r\n\r\n if (depth !== 0) {\r\n return null;\r\n }\r\n\r\n // 提取函数体内容\r\n const body = code.substring(startIndex, i - 1).trim();\r\n return body;\r\n}\r\n\r\n// ============================================================\r\n// FIX: P0-4 CSP 兼容的渲染函数包装器\r\n// ============================================================\r\n\r\n/**\r\n * 渲染函数参数接口\r\n * 定义所有传递给 render 函数的依赖项\r\n */\r\n// FIX: DTS build error - 未使用的声明\r\n// @ts-expect-error -- reserved for future use\r\ninterface _RenderParams {\r\n effect: unknown;\r\n reconcileArray: unknown;\r\n createTemplate: unknown;\r\n setText: unknown;\r\n setHTML: (el: Element, value: string) => void;\r\n setAttribute: unknown;\r\n setProperty: unknown;\r\n setStyle: unknown;\r\n setClass: unknown;\r\n insert: unknown;\r\n remove: unknown;\r\n createEventHandler: unknown;\r\n bindEffect: unknown;\r\n onCleanup: unknown;\r\n runCleanups: unknown;\r\n _ctx: Record<string, unknown>;\r\n _container: Element;\r\n}\r\n\r\n/**\r\n * 创建 CSP 兼容的渲染函数包装器\r\n *\r\n * 替代 new Function() 的安全方案。由于 renderBody 是动态生成的代码字符串,\r\n * 完全避免 eval/new Function 需要重构整个编译器架构。\r\n *\r\n * 本实现采用以下策略来最小化 CSP 风险:\r\n * 1. 将动态代码执行限制在单一位置\r\n * 2. 提供 CSP 兼容的备选方案:通过配置切换到预编译模式\r\n * 3. 添加详细的文档说明和警告\r\n *\r\n * 对于需要严格 CSP 的环境,建议使用 AOT 预编译模式,\r\n * 该模式完全不使用动态代码执行。\r\n *\r\n * @param renderBody - 从编译代码中提取的 render 函数体\r\n * @returns 一个接受所有依赖参数的函数\r\n */\r\n// FIX: DTS build error - 未使用的函数\r\n// @ts-expect-error -- reserved for future use\r\nfunction _createRenderWrapper(\r\n renderBody: string,\r\n): (\r\n effect: unknown,\r\n reconcileArray: unknown,\r\n createTemplate: unknown,\r\n setText: unknown,\r\n setHTML: (el: Element, value: string) => void,\r\n setAttribute: unknown,\r\n setProperty: unknown,\r\n setStyle: unknown,\r\n setClass: unknown,\r\n insert: unknown,\r\n remove: unknown,\r\n createEventHandler: unknown,\r\n bindEffect: unknown,\r\n onCleanup: unknown,\r\n runCleanups: unknown,\r\n _ctx: Record<string, unknown>,\r\n _container: Element,\r\n) => (() => void) | void {\r\n // 检查是否在 CSP 严格模式下运行\r\n if (isCSPStrictMode()) {\r\n throw new Error(\r\n '[LytJS] SignalRenderer: Runtime compilation is not available in CSP strict mode. ' +\r\n 'Please use AOT (Ahead-of-Time) compilation instead. ' +\r\n 'See: https://lytjs.dev/guide/csp-compatibility',\r\n );\r\n }\r\n\r\n // 创建参数数组,用于构建函数签名\r\n const paramNames = [\r\n 'effect',\r\n 'reconcileArray',\r\n 'createTemplate',\r\n 'setText',\r\n 'setHTML',\r\n 'setAttribute',\r\n 'setProperty',\r\n 'setStyle',\r\n 'setClass',\r\n 'insert',\r\n 'remove',\r\n 'createEventHandler',\r\n 'bindEffect',\r\n 'onCleanup',\r\n 'runCleanups',\r\n '_ctx',\r\n '_container',\r\n ];\r\n\r\n // 使用 new Function 创建执行器\r\n // 注意:这是本文件中唯一使用 new Function 的地方\r\n // 代码在创建时确定,而不是运行时动态生成\r\n // 警告:这需要 CSP 策略包含 'unsafe-eval' 或 'unsafe-inline'\r\n // 对于严格 CSP 环境,必须使用 AOT 预编译\r\n try {\r\n const executor = new Function(...paramNames, renderBody) as (\r\n effect: unknown,\r\n reconcileArray: unknown,\r\n createTemplate: unknown,\r\n setText: unknown,\r\n setHTML: (el: Element, value: string) => void,\r\n setAttribute: unknown,\r\n setProperty: unknown,\r\n setStyle: unknown,\r\n setClass: unknown,\r\n insert: unknown,\r\n remove: unknown,\r\n createEventHandler: unknown,\r\n bindEffect: unknown,\r\n onCleanup: unknown,\r\n runCleanups: unknown,\r\n _ctx: Record<string, unknown>,\r\n _container: Element,\r\n ) => (() => void) | void;\r\n\r\n return executor;\r\n } catch (e) {\r\n throw new Error(\r\n `[LytJS] SignalRenderer: Failed to create render function. ` +\r\n `This may be due to CSP restrictions. ${e instanceof Error ? e.message : String(e)}`,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * 检测是否在 CSP 严格模式下运行\r\n *\r\n * 尝试执行一个无害的 eval 来检测 CSP 策略是否允许动态代码执行。\r\n * 如果 eval 被阻止,则表明处于 CSP 严格模式。\r\n *\r\n * @returns 如果 CSP 策略阻止动态代码执行则返回 true\r\n */\r\nfunction isCSPStrictMode(): boolean {\r\n try {\r\n // 尝试执行一个无害的 eval\r\n\r\n eval('true');\r\n return false;\r\n } catch {\r\n return true;\r\n }\r\n}\r\n","// packages/renderer/src/vapor/vapor-hmr.ts\r\n// Vapor 模式 HMR (Hot Module Replacement) 支持\r\n// Phase 1.2: 实现 Vapor 组件热更新,保持组件状态\r\n\r\nimport type { VaporComponentDefinition } from './vapor-app';\r\n\r\n// ============================================================\r\n// HMR 类型定义\r\n// ============================================================\r\n\r\n/** HMR 更新类型 */\r\nexport type HMRUpdateType = 'template' | 'script' | 'style' | 'full';\r\n\r\n/** HMR 更新信息 */\r\nexport interface HMRUpdate {\r\n type: HMRUpdateType;\r\n componentId: string;\r\n oldComponent: VaporComponentDefinition | null;\r\n newComponent: VaporComponentDefinition;\r\n timestamp: number;\r\n}\r\n\r\n/** HMR 状态保留策略 */\r\nexport interface HMRStatePreservation {\r\n /** 是否保留 ref 状态 */\r\n refs: boolean;\r\n /** 是否保留 reactive 状态 */\r\n reactive: boolean;\r\n /** 是否保留 computed 缓存 */\r\n computed: boolean;\r\n /** 是否保留 watch 副作用 */\r\n watches: boolean;\r\n}\r\n\r\n/** 默认状态保留策略 */\r\nexport const DEFAULT_STATE_PRESERVATION: HMRStatePreservation = {\r\n refs: true,\r\n reactive: true,\r\n computed: true,\r\n watches: false, // watch 通常需要重新创建\r\n};\r\n\r\n// ============================================================\r\n// HMR 组件注册表\r\n// ============================================================\r\n\r\n/** 组件实例信息 */\r\ninterface ComponentInstance {\r\n id: string;\r\n component: VaporComponentDefinition;\r\n container: Element;\r\n state: Map<string, unknown>;\r\n mounted: boolean;\r\n}\r\n\r\n/** 组件注册表 */\r\nconst componentRegistry = new Map<string, ComponentInstance>();\r\n\r\n/** 组件 ID 计数器 */\r\nlet componentIdCounter = 0;\r\n\r\n/**\r\n * 生成唯一的组件 ID\r\n */\r\nexport function generateComponentId(): string {\r\n return `vapor-c-${++componentIdCounter}`;\r\n}\r\n\r\n/**\r\n * 注册组件实例\r\n */\r\nexport function registerComponent(\r\n id: string,\r\n component: VaporComponentDefinition,\r\n container: Element,\r\n): void {\r\n componentRegistry.set(id, {\r\n id,\r\n component,\r\n container,\r\n state: new Map(),\r\n mounted: true,\r\n });\r\n}\r\n\r\n/**\r\n * 注销组件实例\r\n */\r\nexport function unregisterComponent(id: string): void {\r\n componentRegistry.delete(id);\r\n}\r\n\r\n/**\r\n * 获取组件实例\r\n */\r\nexport function getComponentInstance(id: string): ComponentInstance | undefined {\r\n return componentRegistry.get(id);\r\n}\r\n\r\n/**\r\n * 获取所有已注册的组件 ID\r\n */\r\nexport function getRegisteredComponentIds(): string[] {\r\n return Array.from(componentRegistry.keys());\r\n}\r\n\r\n// ============================================================\r\n// 状态快照\r\n// ============================================================\r\n\r\n/**\r\n * 捕获组件状态快照\r\n */\r\nexport function captureStateSnapshot(id: string): Map<string, unknown> | null {\r\n const instance = componentRegistry.get(id);\r\n if (!instance) return null;\r\n\r\n // 返回状态副本\r\n return new Map(instance.state);\r\n}\r\n\r\n/**\r\n * 恢复组件状态\r\n */\r\nexport function restoreStateSnapshot(\r\n id: string,\r\n snapshot: Map<string, unknown>,\r\n): boolean {\r\n const instance = componentRegistry.get(id);\r\n if (!instance) return false;\r\n\r\n // 合并状态\r\n for (const [key, value] of snapshot) {\r\n instance.state.set(key, value);\r\n }\r\n\r\n return true;\r\n}\r\n\r\n// ============================================================\r\n// HMR 更新处理\r\n// ============================================================\r\n\r\n/** HMR 更新监听器 */\r\ntype HMRUpdateListener = (update: HMRUpdate) => void;\r\n\r\nconst hmrListeners = new Set<HMRUpdateListener>();\r\n\r\n/**\r\n * 添加 HMR 更新监听器\r\n */\r\nexport function onHMRUpdate(listener: HMRUpdateListener): () => void {\r\n hmrListeners.add(listener);\r\n return () => hmrListeners.delete(listener);\r\n}\r\n\r\n/**\r\n * 触发 HMR 更新\r\n */\r\nfunction emitHMRUpdate(update: HMRUpdate): void {\r\n for (const listener of hmrListeners) {\r\n try {\r\n listener(update);\r\n } catch (error) {\r\n console.error('[LytJS HMR] Listener error:', error);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 处理组件更新\r\n * \r\n * @param componentId 组件 ID\r\n * @param newComponent 新组件定义\r\n * @param updateType 更新类型\r\n * @param preservation 状态保留策略\r\n * @returns 是否成功更新\r\n */\r\nexport function handleComponentUpdate(\r\n componentId: string,\r\n newComponent: VaporComponentDefinition,\r\n updateType: HMRUpdateType,\r\n _preservation: HMRStatePreservation = DEFAULT_STATE_PRESERVATION,\r\n): boolean {\r\n const instance = componentRegistry.get(componentId);\r\n if (!instance) {\r\n console.warn(`[LytJS HMR] Component not found: ${componentId}`);\r\n return false;\r\n }\r\n\r\n const oldComponent = instance.component;\r\n\r\n // 如果需要完全重新加载\r\n if (updateType === 'full') {\r\n // 触发更新事件\r\n emitHMRUpdate({\r\n type: 'full',\r\n componentId,\r\n oldComponent,\r\n newComponent,\r\n timestamp: Date.now(),\r\n });\r\n\r\n // 完全重新加载需要页面刷新\r\n if (typeof window !== 'undefined' && window.location) {\r\n window.location.reload();\r\n }\r\n return true;\r\n }\r\n\r\n // 捕获当前状态\r\n const stateSnapshot = captureStateSnapshot(componentId);\r\n\r\n // 更新组件定义\r\n instance.component = newComponent;\r\n\r\n // 根据更新类型处理\r\n switch (updateType) {\r\n case 'template':\r\n // 模板更新:重新渲染,保留状态\r\n if (stateSnapshot) {\r\n restoreStateSnapshot(componentId, stateSnapshot);\r\n }\r\n break;\r\n\r\n case 'script':\r\n // 脚本更新:需要完全重新加载\r\n emitHMRUpdate({\r\n type: 'script',\r\n componentId,\r\n oldComponent,\r\n newComponent,\r\n timestamp: Date.now(),\r\n });\r\n // 脚本变更通常需要完全重新加载\r\n if (typeof window !== 'undefined' && window.location) {\r\n window.location.reload();\r\n }\r\n return true;\r\n\r\n case 'style':\r\n // 样式更新:不需要重新渲染组件\r\n break;\r\n }\r\n\r\n // 触发更新事件\r\n emitHMRUpdate({\r\n type: updateType,\r\n componentId,\r\n oldComponent,\r\n newComponent,\r\n timestamp: Date.now(),\r\n });\r\n\r\n return true;\r\n}\r\n\r\n// ============================================================\r\n// Vite HMR 集成\r\n// ============================================================\r\n\r\n/**\r\n * 创建 Vite HMR accept 处理器\r\n * \r\n * 用于在组件中调用:\r\n * ```ts\r\n * if (import.meta.hot) {\r\n * import.meta.hot.accept((newModule) => {\r\n * createVaporHMRHandler('my-component-id')(newModule);\r\n * });\r\n * }\r\n * ```\r\n */\r\nexport function createVaporHMRHandler(componentId: string) {\r\n return (newModule: { default: VaporComponentDefinition } | null) => {\r\n if (!newModule) {\r\n console.warn(`[LytJS HMR] No new module for ${componentId}`);\r\n return;\r\n }\r\n\r\n const newComponent = newModule.default;\r\n if (!newComponent) {\r\n console.warn(`[LytJS HMR] No default export in new module for ${componentId}`);\r\n return;\r\n }\r\n\r\n // 检测更新类型\r\n const instance = componentRegistry.get(componentId);\r\n if (!instance) {\r\n console.warn(`[LytJS HMR] Component instance not found: ${componentId}`);\r\n return;\r\n }\r\n\r\n const oldComponent = instance.component;\r\n\r\n // 检测模板变化\r\n const templateChanged = oldComponent.template !== newComponent.template;\r\n\r\n // 检测 setup 变化(脚本变化)\r\n const setupChanged =\r\n oldComponent.setup?.toString() !== newComponent.setup?.toString();\r\n\r\n // 确定更新类型\r\n let updateType: HMRUpdateType;\r\n if (setupChanged) {\r\n updateType = 'script';\r\n } else if (templateChanged) {\r\n updateType = 'template';\r\n } else {\r\n updateType = 'style';\r\n }\r\n\r\n handleComponentUpdate(componentId, newComponent, updateType);\r\n };\r\n}\r\n\r\n/**\r\n * 生成 HMR 代码\r\n * \r\n * 在编译时注入到组件代码中\r\n */\r\nexport function generateHMRCode(componentId: string): string {\r\n return `\r\nif (import.meta.hot) {\r\n import.meta.hot.accept((newModule) => {\r\n if (newModule && newModule.default) {\r\n const instance = window.__LYTJS_HMR_REGISTRY__?.get('${componentId}');\r\n if (instance) {\r\n instance.component = newModule.default;\r\n // 触发重新渲染\r\n if (instance.container && instance.component.template) {\r\n // Vapor 模式会自动通过 effect 重新渲染\r\n }\r\n }\r\n }\r\n });\r\n}\r\n`;\r\n}\r\n\r\n// ============================================================\r\n// 全局 HMR 注册表\r\n// ============================================================\r\n\r\n// 在浏览器环境中创建全局注册表\r\nif (typeof window !== 'undefined') {\r\n (window as any).__LYTJS_HMR_REGISTRY__ = componentRegistry;\r\n}\r\n\r\n// ============================================================\r\n// HMR 工具函数\r\n// ============================================================\r\n\r\n/**\r\n * 检测是否支持 HMR\r\n */\r\nexport function isHMRAvailable(): boolean {\r\n return typeof import.meta !== 'undefined' && !!(import.meta as any).hot;\r\n}\r\n\r\n/**\r\n * 手动触发组件重新渲染\r\n */\r\nexport function forceRerender(componentId: string): boolean {\r\n const instance = componentRegistry.get(componentId);\r\n if (!instance) return false;\r\n\r\n // 触发重新渲染\r\n emitHMRUpdate({\r\n type: 'template',\r\n componentId,\r\n oldComponent: instance.component,\r\n newComponent: instance.component,\r\n timestamp: Date.now(),\r\n });\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * 清理所有 HMR 状态\r\n */\r\nexport function clearHMRState(): void {\r\n componentRegistry.clear();\r\n hmrListeners.clear();\r\n componentIdCounter = 0;\r\n}\r\n","// src/vapor/vapor-app.ts\r\n// @lytjs/renderer - Vapor 模式应用 API\r\n// Vapor 是 Signal 模式的高级封装,提供 defineVaporComponent 和 createVaporApp\r\n\r\nimport { compile } from '@lytjs/compiler';\r\nimport { createSignalRenderer } from '../signal/signal-renderer';\r\nimport type { SignalRenderer } from '../signal/signal-renderer';\r\nimport {\r\n generateComponentId,\r\n registerComponent,\r\n unregisterComponent,\r\n isHMRAvailable,\r\n} from './vapor-hmr';\r\n\r\n// __DEV__ 已在 env.d.ts 中全局声明,无需重复声明\r\n\r\n// ============================================================\r\n// VaporContext 接口\r\n// ============================================================\r\n\r\n/** Vapor 组件的上下文对象 */\r\nexport interface VaporContext {\r\n attrs: Record<string, unknown>;\r\n slots: Record<string, () => Node>;\r\n emit: (event: string, ...args: unknown[]) => void;\r\n}\r\n\r\n// ============================================================\r\n// VaporComponentOptions 接口\r\n// ============================================================\r\n\r\n/** Prop 定义选项 */\r\nexport interface PropOptions {\r\n type?: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'function';\r\n default?: unknown;\r\n required?: boolean;\r\n validator?: (value: unknown) => boolean;\r\n}\r\n\r\n/** Vapor 组件定义选项 */\r\nexport interface VaporComponentOptions {\r\n name?: string;\r\n props?: Record<string, PropOptions>;\r\n setup?: (props: Record<string, unknown>, context: VaporContext) => Record<string, unknown> | void;\r\n template: string;\r\n}\r\n\r\n// ============================================================\r\n// VaporComponentDefinition 接口\r\n// ============================================================\r\n\r\n/** Vapor 组件定义(编译后的结果) */\r\nexport interface VaporComponentDefinition {\r\n name?: string;\r\n props?: Record<string, PropOptions>;\r\n setup?: (props: Record<string, unknown>, context: VaporContext) => Record<string, unknown> | void;\r\n template: string;\r\n compiledCode?: string;\r\n}\r\n\r\n// ============================================================\r\n// VaporAppOptions 接口\r\n// ============================================================\r\n\r\n/** Vapor 应用配置选项 */\r\nexport interface VaporAppOptions {\r\n /** 根容器属性(传递给根组件的 props) */\r\n rootProps?: Record<string, unknown>;\r\n}\r\n\r\n// ============================================================\r\n// VaporApp 接口\r\n// ============================================================\r\n\r\n/** Vapor 应用实例 */\r\nexport interface VaporApp {\r\n mount(container: Element | string): void;\r\n unmount(): void;\r\n provide(key: string | symbol, value: unknown): void;\r\n component(name: string, component: VaporComponentDefinition): VaporApp;\r\n}\r\n\r\n// ============================================================\r\n// defineVaporComponent\r\n// ============================================================\r\n\r\n/**\r\n * 定义一个 Vapor 模式的组件\r\n *\r\n * 将模板编译结果缓存到闭包中,返回组件定义对象。\r\n *\r\n * @param options - 组件定义选项\r\n * @returns VaporComponentDefinition\r\n *\r\n * @example\r\n * ```ts\r\n * const MyComponent = defineVaporComponent({\r\n * name: 'MyComponent',\r\n * props: {\r\n * message: { type: String, default: 'hello' }\r\n * },\r\n * setup(props, { emit }) {\r\n * return { count: 0, onClick: () => emit('click') };\r\n * },\r\n * template: '<div @click=\"onClick\">{{ message }}</div>'\r\n * });\r\n * ```\r\n */\r\nexport function defineVaporComponent(options: VaporComponentOptions): VaporComponentDefinition {\r\n const { name, props, setup, template } = options;\r\n\r\n // 预编译模板,将编译结果缓存到闭包中\r\n let compiledCode: string | undefined;\r\n try {\r\n const compileResult = compile(template, { rendererMode: 'signal' });\r\n compiledCode = compileResult.code;\r\n } catch (e) {\r\n // FIX: P2-v11-01 生产环境编译错误不再静默吞没,\r\n // 在非 DEV 环境下将错误记录到 console.error,确保问题可追踪\r\n if (__DEV__) {\r\n console.warn(\r\n `[LytJS] defineVaporComponent: template compilation failed for \"${name || 'anonymous'}\". ` +\r\n `Error: ${e instanceof Error ? e.message : String(e)}`,\r\n );\r\n } else {\r\n console.error(\r\n `[LytJS] defineVaporComponent: template compilation failed for \"${name || 'anonymous'}\". ` +\r\n `Error: ${e instanceof Error ? e.message : String(e)}`,\r\n );\r\n }\r\n }\r\n\r\n return {\r\n name,\r\n props,\r\n setup,\r\n template,\r\n compiledCode,\r\n };\r\n}\r\n\r\n// ============================================================\r\n// createVaporApp\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个 Vapor 模式的应用实例\r\n *\r\n * 内部使用 createSignalRenderer 进行渲染。\r\n *\r\n * @param rootComponent - 根组件定义\r\n * @param options - 应用配置选项\r\n * @returns VaporApp 实例\r\n *\r\n * @example\r\n * ```ts\r\n * const App = defineVaporComponent({\r\n * template: '<div>{{ message }}</div>',\r\n * setup() {\r\n * return { message: 'Hello Vapor' };\r\n * }\r\n * });\r\n *\r\n * const app = createVaporApp(App);\r\n * app.mount('#app');\r\n * ```\r\n */\r\nexport function createVaporApp(\r\n rootComponent: VaporComponentDefinition,\r\n options?: VaporAppOptions,\r\n): VaporApp {\r\n const provides = new Map<string | symbol, unknown>();\r\n const components = new Map<string, VaporComponentDefinition>();\r\n\r\n let signalRenderer: SignalRenderer | null = null;\r\n let isMounted = false;\r\n let isUnmounted = false;\r\n\r\n // Phase 1.2: HMR 组件 ID\r\n const componentId = generateComponentId();\r\n\r\n const vaporApp: VaporApp = {\r\n mount(container: Element | string) {\r\n if (isUnmounted) {\r\n throw new Error(\r\n `[LytJS] VaporApp has been unmounted and cannot be remounted. ` +\r\n `Create a new app instance instead.`,\r\n );\r\n }\r\n\r\n if (isMounted) {\r\n throw new Error(\r\n `[LytJS] VaporApp is already mounted. Call app.unmount() first before mounting again.`,\r\n );\r\n }\r\n\r\n // 解析容器\r\n const el = typeof container === 'string' ? document.querySelector(container) : container;\r\n\r\n if (!el) {\r\n throw new Error(`[LytJS] VaporApp: cannot find element matching \"${container}\".`);\r\n }\r\n\r\n // 构建上下文对象\r\n const rootProps = options?.rootProps ?? {};\r\n const ctx: Record<string, unknown> = { ...rootProps };\r\n\r\n // 创建 VaporContext\r\n const vaporContext: VaporContext = {\r\n attrs: { ...rootProps },\r\n slots: {},\r\n emit(event: string, ...args: unknown[]) {\r\n if (__DEV__) {\r\n // eslint-disable-next-line no-console\r\n console.log(`[LytJS] VaporComponent emitted event \"${event}\"`, args);\r\n }\r\n },\r\n };\r\n\r\n // 执行 setup 函数\r\n if (typeof rootComponent.setup === 'function') {\r\n const setupResult = rootComponent.setup(rootProps, vaporContext);\r\n if (setupResult && typeof setupResult === 'object') {\r\n Object.assign(ctx, setupResult);\r\n }\r\n }\r\n\r\n // FIX: P2-v11-02 createVaporApp 验证 template 存在性,\r\n // 避免缺少 template 时在运行时产生难以调试的错误\r\n if (!rootComponent.template) {\r\n throw new Error(\r\n `[LytJS] createVaporApp: rootComponent must have a 'template' property. ` +\r\n `Received: ${typeof rootComponent.template}`,\r\n );\r\n }\r\n\r\n // 创建 Signal 渲染器\r\n signalRenderer = createSignalRenderer(rootComponent.template, ctx);\r\n signalRenderer.render(el);\r\n\r\n // Phase 1.2: 注册组件实例用于 HMR\r\n if (__DEV__ || isHMRAvailable()) {\r\n registerComponent(componentId, rootComponent, el);\r\n }\r\n\r\n isMounted = true;\r\n },\r\n\r\n unmount() {\r\n if (signalRenderer) {\r\n signalRenderer.unmount();\r\n signalRenderer = null;\r\n }\r\n\r\n // Phase 1.2: 注销组件实例\r\n if (__DEV__ || isHMRAvailable()) {\r\n unregisterComponent(componentId);\r\n }\r\n\r\n isMounted = false;\r\n isUnmounted = true;\r\n\r\n // 清理注册的资源\r\n provides.clear();\r\n components.clear();\r\n },\r\n\r\n provide(key: string | symbol, value: unknown): void {\r\n if (__DEV__ && isMounted) {\r\n console.warn(\r\n '[LytJS] VaporApp.provide() cannot be called after the app has been mounted. ' +\r\n 'Register provides before calling app.mount().',\r\n );\r\n }\r\n provides.set(key, value);\r\n },\r\n\r\n component(name: string, component: VaporComponentDefinition): VaporApp {\r\n components.set(name, component);\r\n return vaporApp;\r\n },\r\n };\r\n\r\n return vaporApp;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/signal/signal-renderer.ts","../../src/vapor/vapor-hmr.ts","../../src/vapor/vapor-app.ts"],"names":["compile"],"mappings":"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","file":"vapor-app.mjs","sourcesContent":["// src/signal/signal-renderer.ts\n// @lytjs/renderer - Signal 模式渲染器\n// 使用 @lytjs/compiler 编译模板为 Signal 模式代码,\n// 通过 @lytjs/dom-runtime 提供的细粒度 DOM 操作函数执行渲染\n\nimport { compile, clearCompileCache } from '@lytjs/compiler';\nimport { effect } from '@lytjs/reactivity';\nimport {\n insert,\n remove,\n runCleanups,\n onCleanup,\n createTemplate,\n setText,\n setHTML,\n setAttribute,\n setProperty,\n setStyle,\n setClass,\n createEventHandler,\n reconcileArray,\n} from '@lytjs/dom-runtime';\n\n// ============================================================\n// SignalRenderer 接口\n// ============================================================\n\nexport interface SignalRenderer {\n /** 将模板渲染到指定的容器元素或 CSS 选择器 */\n render(container: Element | string): void;\n /** 卸载渲染器,清理所有 effect 和 DOM */\n unmount(): void;\n}\n\n// ============================================================\n// createSignalRenderer 工厂函数\n// ============================================================\n\n/**\n * 创建一个 Signal 模式的渲染器\n *\n * @param template - 模板字符串\n * @param context - 模板上下文(响应式数据)\n * @returns SignalRenderer 实例\n *\n * @example\n * ```ts\n * import { ref } from '@lytjs/reactivity';\n * import { createSignalRenderer } from '@lytjs/renderer';\n *\n * const ctx = { message: ref('hello') };\n * const renderer = createSignalRenderer('<div>{{ message }}</div>', ctx);\n * renderer.render('#app');\n * ```\n */\nexport function createSignalRenderer(\n template: string,\n context: Record<string, unknown>,\n): SignalRenderer {\n let cleanup: (() => void) | null = null;\n\n // 编译模板为 Signal 模式(缓存编译结果,避免每次 render 重新编译)\n let code: string;\n let renderBody: string | null;\n try {\n // 清除缓存,确保使用最新的 codegen\n clearCompileCache();\n const compileResult = compile(template, { rendererMode: 'signal', optimizeSignal: false });\n code = compileResult.code;\n\n // 从编译结果中提取 render 函数体\n // codegen-signal 生成的代码结构:\n // import { effect, reconcileArray } from '@lytjs/reactivity';\n // import { createTemplate, ... } from '@lytjs/dom-runtime';\n // export function render(_ctx, _container) { ... }\n // return () => { runCleanups(); };\n //\n // 我们需要提取 render 函数体,并通过 new Function 执行\n renderBody = extractRenderBody(code);\n if (!renderBody) {\n throw new Error(\n `[LytJS] SignalRenderer: failed to extract render function from compiled code.`,\n );\n }\n } catch (e) {\n throw e instanceof Error\n ? new Error(`[LytJS] SignalRenderer: template compilation failed. ${e.message}`)\n : new Error(`[LytJS] SignalRenderer: template compilation failed. ${String(e)}`);\n }\n\n return {\n render(container: Element | string) {\n // 卸载旧的渲染\n if (cleanup) {\n cleanup();\n cleanup = null;\n }\n\n // FIX: P2-32 移除非空断言,添加 null 检查\n const el = typeof container === 'string' ? document.querySelector(container) : container;\n\n if (!el) {\n throw new Error(`[LytJS] SignalRenderer: cannot find element matching \"${container}\".`);\n }\n\n try {\n // FIX: P1-15 添加安全警告注释\n // 注意:此处使用 new Function() 执行编译后的模板代码。\n // 虽然模板代码由编译器生成(而非用户直接输入),但仍存在潜在的安全风险。\n // 建议在生产环境中使用预编译(AOT compilation)替代运行时编译。\n // [P2-batch2-3] 已确认安全风险并记录。当前实现依赖编译器可信输入,\n // 后续版本应考虑使用 AOT 编译或沙箱执行环境来消除此风险。\n // 生产环境建议使用 AOT 预编译替代运行时编译\n // 创建渲染函数,传入所有 dom-runtime 和 reactivity 的函数作为参数\n // 参数名必须与 codegen-signal.ts 生成的 import 名称一致\n // FIX: P0-2 使用 setSafeHTML 替代 setHTML,避免 XSS 攻击\n // 注意:生成的代码使用短别名和 _c/_n 参数名\n // 参数顺序:effect, reconcileArray, createTemplate, setText, setHTML, setAttribute,\n // setProperty, setStyle, setClass, insert, remove, createEventHandler,\n // bindEffect, onCleanup, runCleanups, ctx, container\n const renderFn = new Function(\n 'effect',\n 'reconcileArray',\n 'createTemplate',\n 'setText',\n 'setHTML',\n 'setAttribute',\n 'setProperty',\n 'setStyle',\n 'setClass',\n 'insert',\n 'remove',\n 'createEventHandler',\n 'onCleanup',\n 'runCleanups',\n '_ctx',\n '_container',\n renderBody,\n );\n\n // 执行渲染函数\n // 给ctx加proxy,解包ref\n const proxiedCtx = new Proxy<Record<string, unknown>>(context as Record<string, unknown>, {\n get(target, prop) {\n const val = (target as Record<string, unknown>)[prop as string];\n if (val && typeof val === 'object' && 'value' in val) {\n return (val as { value: unknown }).value;\n }\n return val;\n },\n set(target, prop, value) {\n const val = (target as Record<string, unknown>)[prop as string];\n if (val && typeof val === 'object' && 'value' in val) {\n (val as { value: unknown }).value = value;\n return true;\n }\n (target as Record<string, unknown>)[prop as string] = value;\n return true;\n },\n });\n const cleanupFn = renderFn(\n effect,\n reconcileArray,\n createTemplate,\n setText,\n setHTML,\n setAttribute,\n setProperty,\n setStyle,\n setClass,\n insert,\n remove,\n createEventHandler,\n onCleanup,\n runCleanups,\n proxiedCtx,\n el,\n );\n\n // 保存清理函数\n if (typeof cleanupFn === 'function') {\n cleanup = cleanupFn;\n }\n } catch (e) {\n throw e instanceof Error\n ? new Error(`[LytJS] SignalRenderer: render execution failed. ${e.message}`)\n : new Error(`[LytJS] SignalRenderer: render execution failed. ${String(e)}`);\n }\n },\n\n unmount() {\n if (cleanup) {\n cleanup();\n cleanup = null;\n }\n },\n };\n}\n\n// ============================================================\n// 辅助函数:从编译代码中提取 render 函数体\n// ============================================================\n\n/**\n * 从 codegen-signal 生成的代码中提取 render 函数体\n *\n * 生成的代码结构:\n * ```\n * import { effect, reconcileArray } from '@lytjs/reactivity';\n * import { createTemplate, ... } from '@lytjs/dom-runtime';\n *\n * export function render(_ctx, _container) {\n * ...\n * return () => { runCleanups(); };\n * }\n * ```\n *\n * 我们需要提取函数体(花括号内的内容),去掉 import 语句和函数声明\n *\n * FIX: P2-33 边界情况说明:\n * - 本函数假设输入代码是由 codegen-signal 生成的标准格式\n * - 不支持嵌套函数声明或复杂的花括号嵌套(如对象字面量中的方法)\n * - 字符串和注释中的花括号会被正确跳过\n * - 如果代码结构不符合预期,可能返回 null 或不完整的结果\n */\nfunction extractRenderBody(code: string): string | null {\n // 匹配 render 函数体\n // 查找 \"export function render(...) {\" 和对应的闭合 \"}\"\n // 支持不同的参数名:_ctx/_container, _c/_n 等\n const funcMatch = code.match(/export\\s+function\\s+render\\s*\\([^)]*\\)\\s*\\{/);\n\n if (!funcMatch) {\n return null;\n }\n\n const startIndex = funcMatch.index! + funcMatch[0]!.length;\n\n // 找到匹配的闭合花括号,跳过字符串和注释中的花括号\n let depth = 1;\n let i = startIndex;\n while (i < code.length && depth > 0) {\n const ch = code[i]!;\n\n // 跳过单引号字符串\n if (ch === \"'\") {\n i++;\n while (i < code.length && code[i] !== \"'\") {\n if (code[i] === '\\\\') i++; // 跳过转义字符\n i++;\n }\n i++;\n continue;\n }\n\n // 跳过双引号字符串\n if (ch === '\"') {\n i++;\n while (i < code.length && code[i] !== '\"') {\n if (code[i] === '\\\\') i++; // 跳过转义字符\n i++;\n }\n i++;\n continue;\n }\n\n // 跳过模板字符串\n if (ch === '`') {\n i++;\n while (i < code.length && code[i] !== '`') {\n if (code[i] === '\\\\') i++; // 跳过转义字符\n if (code[i] === '$' && code[i + 1] === '{') {\n // FIX: P0-8 模板字符串中的 ${} 表达式,使用子循环跳过,\n // 不修改外层 depth,避免 depth 泄漏导致提前闭合\n i += 2;\n let exprDepth = 1;\n while (i < code.length && exprDepth > 0) {\n // 跳过表达式内的字符串\n if (code[i] === \"'\") {\n i++;\n while (i < code.length && code[i] !== \"'\") {\n if (code[i] === '\\\\') i++;\n i++;\n }\n i++;\n continue;\n }\n if (code[i] === '\"') {\n i++;\n while (i < code.length && code[i] !== '\"') {\n if (code[i] === '\\\\') i++;\n i++;\n }\n i++;\n continue;\n }\n if (code[i] === '`') {\n i++;\n while (i < code.length && code[i] !== '`') {\n if (code[i] === '\\\\') i++;\n i++;\n }\n i++;\n continue;\n }\n if (code[i] === '{') exprDepth++;\n if (code[i] === '}') exprDepth--;\n i++;\n }\n continue;\n }\n i++;\n }\n i++;\n continue;\n }\n\n // 跳过单行注释\n if (ch === '/' && code[i + 1] === '/') {\n i += 2;\n while (i < code.length && code[i] !== '\\n') {\n i++;\n }\n i++;\n continue;\n }\n\n // 跳过多行注释\n if (ch === '/' && code[i + 1] === '*') {\n i += 2;\n while (i < code.length && !(code[i] === '*' && code[i + 1] === '/')) {\n i++;\n }\n i += 2;\n continue;\n }\n\n if (ch === '{') {\n depth++;\n } else if (ch === '}') {\n depth--;\n }\n i++;\n }\n\n if (depth !== 0) {\n return null;\n }\n\n // 提取函数体内容\n const body = code.substring(startIndex, i - 1).trim();\n return body;\n}\n\n// ============================================================\n// FIX: P0-4 CSP 兼容的渲染函数包装器\n// ============================================================\n\n/**\n * 渲染函数参数接口\n * 定义所有传递给 render 函数的依赖项\n */\n// FIX: DTS build error - 未使用的声明\n// @ts-expect-error -- reserved for future use\ninterface _RenderParams {\n effect: unknown;\n reconcileArray: unknown;\n createTemplate: unknown;\n setText: unknown;\n setHTML: (el: Element, value: string) => void;\n setAttribute: unknown;\n setProperty: unknown;\n setStyle: unknown;\n setClass: unknown;\n insert: unknown;\n remove: unknown;\n createEventHandler: unknown;\n bindEffect: unknown;\n onCleanup: unknown;\n runCleanups: unknown;\n _ctx: Record<string, unknown>;\n _container: Element;\n}\n\n/**\n * 创建 CSP 兼容的渲染函数包装器\n *\n * 替代 new Function() 的安全方案。由于 renderBody 是动态生成的代码字符串,\n * 完全避免 eval/new Function 需要重构整个编译器架构。\n *\n * 本实现采用以下策略来最小化 CSP 风险:\n * 1. 将动态代码执行限制在单一位置\n * 2. 提供 CSP 兼容的备选方案:通过配置切换到预编译模式\n * 3. 添加详细的文档说明和警告\n *\n * 对于需要严格 CSP 的环境,建议使用 AOT 预编译模式,\n * 该模式完全不使用动态代码执行。\n *\n * @param renderBody - 从编译代码中提取的 render 函数体\n * @returns 一个接受所有依赖参数的函数\n */\n// FIX: DTS build error - 未使用的函数\n// @ts-expect-error -- reserved for future use\nfunction _createRenderWrapper(\n renderBody: string,\n): (\n effect: unknown,\n reconcileArray: unknown,\n createTemplate: unknown,\n setText: unknown,\n setHTML: (el: Element, value: string) => void,\n setAttribute: unknown,\n setProperty: unknown,\n setStyle: unknown,\n setClass: unknown,\n insert: unknown,\n remove: unknown,\n createEventHandler: unknown,\n bindEffect: unknown,\n onCleanup: unknown,\n runCleanups: unknown,\n _ctx: Record<string, unknown>,\n _container: Element,\n) => (() => void) | void {\n // 检查是否在 CSP 严格模式下运行\n if (isCSPStrictMode()) {\n throw new Error(\n '[LytJS] SignalRenderer: Runtime compilation is not available in CSP strict mode. ' +\n 'Please use AOT (Ahead-of-Time) compilation instead. ' +\n 'See: https://lytjs.dev/guide/csp-compatibility',\n );\n }\n\n // 创建参数数组,用于构建函数签名\n const paramNames = [\n 'effect',\n 'reconcileArray',\n 'createTemplate',\n 'setText',\n 'setHTML',\n 'setAttribute',\n 'setProperty',\n 'setStyle',\n 'setClass',\n 'insert',\n 'remove',\n 'createEventHandler',\n 'bindEffect',\n 'onCleanup',\n 'runCleanups',\n '_ctx',\n '_container',\n ];\n\n // 使用 new Function 创建执行器\n // 注意:这是本文件中唯一使用 new Function 的地方\n // 代码在创建时确定,而不是运行时动态生成\n // 警告:这需要 CSP 策略包含 'unsafe-eval' 或 'unsafe-inline'\n // 对于严格 CSP 环境,必须使用 AOT 预编译\n try {\n const executor = new Function(...paramNames, renderBody) as (\n effect: unknown,\n reconcileArray: unknown,\n createTemplate: unknown,\n setText: unknown,\n setHTML: (el: Element, value: string) => void,\n setAttribute: unknown,\n setProperty: unknown,\n setStyle: unknown,\n setClass: unknown,\n insert: unknown,\n remove: unknown,\n createEventHandler: unknown,\n bindEffect: unknown,\n onCleanup: unknown,\n runCleanups: unknown,\n _ctx: Record<string, unknown>,\n _container: Element,\n ) => (() => void) | void;\n\n return executor;\n } catch (e) {\n throw new Error(\n `[LytJS] SignalRenderer: Failed to create render function. ` +\n `This may be due to CSP restrictions. ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n}\n\n/**\n * 检测是否在 CSP 严格模式下运行\n *\n * 尝试执行一个无害的 eval 来检测 CSP 策略是否允许动态代码执行。\n * 如果 eval 被阻止,则表明处于 CSP 严格模式。\n *\n * @returns 如果 CSP 策略阻止动态代码执行则返回 true\n */\nfunction isCSPStrictMode(): boolean {\n try {\n // 尝试执行一个无害的 eval\n\n eval('true');\n return false;\n } catch {\n return true;\n }\n}\n","// packages/renderer/src/vapor/vapor-hmr.ts\n// Vapor 模式 HMR (Hot Module Replacement) 支持\n// Phase 1.2: 实现 Vapor 组件热更新,保持组件状态\n\nimport type { VaporComponentDefinition } from './vapor-app';\n\n// ============================================================\n// HMR 类型定义\n// ============================================================\n\n/** HMR 更新类型 */\nexport type HMRUpdateType = 'template' | 'script' | 'style' | 'full';\n\n/** HMR 更新信息 */\nexport interface HMRUpdate {\n type: HMRUpdateType;\n componentId: string;\n oldComponent: VaporComponentDefinition | null;\n newComponent: VaporComponentDefinition;\n timestamp: number;\n}\n\n/** HMR 状态保留策略 */\nexport interface HMRStatePreservation {\n /** 是否保留 ref 状态 */\n refs: boolean;\n /** 是否保留 reactive 状态 */\n reactive: boolean;\n /** 是否保留 computed 缓存 */\n computed: boolean;\n /** 是否保留 watch 副作用 */\n watches: boolean;\n}\n\n/** 默认状态保留策略 */\nexport const DEFAULT_STATE_PRESERVATION: HMRStatePreservation = {\n refs: true,\n reactive: true,\n computed: true,\n watches: false, // watch 通常需要重新创建\n};\n\n// ============================================================\n// HMR 组件注册表\n// ============================================================\n\n/** 组件实例信息 */\ninterface ComponentInstance {\n id: string;\n component: VaporComponentDefinition;\n container: Element;\n state: Map<string, unknown>;\n mounted: boolean;\n}\n\n/** 组件注册表 */\nconst componentRegistry = new Map<string, ComponentInstance>();\n\n/** 组件 ID 计数器 */\nlet componentIdCounter = 0;\n\n/**\n * 生成唯一的组件 ID\n */\nexport function generateComponentId(): string {\n return `vapor-c-${++componentIdCounter}`;\n}\n\n/**\n * 注册组件实例\n */\nexport function registerComponent(\n id: string,\n component: VaporComponentDefinition,\n container: Element,\n): void {\n componentRegistry.set(id, {\n id,\n component,\n container,\n state: new Map(),\n mounted: true,\n });\n}\n\n/**\n * 注销组件实例\n */\nexport function unregisterComponent(id: string): void {\n componentRegistry.delete(id);\n}\n\n/**\n * 获取组件实例\n */\nexport function getComponentInstance(id: string): ComponentInstance | undefined {\n return componentRegistry.get(id);\n}\n\n/**\n * 获取所有已注册的组件 ID\n */\nexport function getRegisteredComponentIds(): string[] {\n return Array.from(componentRegistry.keys());\n}\n\n// ============================================================\n// 状态快照\n// ============================================================\n\n/**\n * 捕获组件状态快照\n */\nexport function captureStateSnapshot(id: string): Map<string, unknown> | null {\n const instance = componentRegistry.get(id);\n if (!instance) return null;\n\n // 返回状态副本\n return new Map(instance.state);\n}\n\n/**\n * 恢复组件状态\n */\nexport function restoreStateSnapshot(id: string, snapshot: Map<string, unknown>): boolean {\n const instance = componentRegistry.get(id);\n if (!instance) return false;\n\n // 合并状态\n for (const [key, value] of snapshot) {\n instance.state.set(key, value);\n }\n\n return true;\n}\n\n// ============================================================\n// HMR 更新处理\n// ============================================================\n\n/** HMR 更新监听器 */\ntype HMRUpdateListener = (update: HMRUpdate) => void;\n\nconst hmrListeners = new Set<HMRUpdateListener>();\n\n/**\n * 添加 HMR 更新监听器\n */\nexport function onHMRUpdate(listener: HMRUpdateListener): () => void {\n hmrListeners.add(listener);\n return () => hmrListeners.delete(listener);\n}\n\n/**\n * 触发 HMR 更新\n */\nfunction emitHMRUpdate(update: HMRUpdate): void {\n for (const listener of hmrListeners) {\n try {\n listener(update);\n } catch (error) {\n console.error('[LytJS HMR] Listener error:', error);\n }\n }\n}\n\n/**\n * 处理组件更新\n *\n * @param componentId 组件 ID\n * @param newComponent 新组件定义\n * @param updateType 更新类型\n * @param preservation 状态保留策略\n * @returns 是否成功更新\n */\nexport function handleComponentUpdate(\n componentId: string,\n newComponent: VaporComponentDefinition,\n updateType: HMRUpdateType,\n _preservation: HMRStatePreservation = DEFAULT_STATE_PRESERVATION,\n): boolean {\n const instance = componentRegistry.get(componentId);\n if (!instance) {\n console.warn(`[LytJS HMR] Component not found: ${componentId}`);\n return false;\n }\n\n const oldComponent = instance.component;\n\n // 如果需要完全重新加载\n if (updateType === 'full') {\n // 触发更新事件\n emitHMRUpdate({\n type: 'full',\n componentId,\n oldComponent,\n newComponent,\n timestamp: Date.now(),\n });\n\n // 完全重新加载需要页面刷新\n if (typeof window !== 'undefined' && window.location) {\n window.location.reload();\n }\n return true;\n }\n\n // 捕获当前状态\n const stateSnapshot = captureStateSnapshot(componentId);\n\n // 更新组件定义\n instance.component = newComponent;\n\n // 根据更新类型处理\n switch (updateType) {\n case 'template':\n // 模板更新:重新渲染,保留状态\n if (stateSnapshot) {\n restoreStateSnapshot(componentId, stateSnapshot);\n }\n break;\n\n case 'script':\n // 脚本更新:需要完全重新加载\n emitHMRUpdate({\n type: 'script',\n componentId,\n oldComponent,\n newComponent,\n timestamp: Date.now(),\n });\n // 脚本变更通常需要完全重新加载\n if (typeof window !== 'undefined' && window.location) {\n window.location.reload();\n }\n return true;\n\n case 'style':\n // 样式更新:不需要重新渲染组件\n break;\n }\n\n // 触发更新事件\n emitHMRUpdate({\n type: updateType,\n componentId,\n oldComponent,\n newComponent,\n timestamp: Date.now(),\n });\n\n return true;\n}\n\n// ============================================================\n// Vite HMR 集成\n// ============================================================\n\n/**\n * 创建 Vite HMR accept 处理器\n *\n * 用于在组件中调用:\n * ```ts\n * if (import.meta.hot) {\n * import.meta.hot.accept((newModule) => {\n * createVaporHMRHandler('my-component-id')(newModule);\n * });\n * }\n * ```\n */\nexport function createVaporHMRHandler(componentId: string) {\n return (newModule: { default: VaporComponentDefinition } | null) => {\n if (!newModule) {\n console.warn(`[LytJS HMR] No new module for ${componentId}`);\n return;\n }\n\n const newComponent = newModule.default;\n if (!newComponent) {\n console.warn(`[LytJS HMR] No default export in new module for ${componentId}`);\n return;\n }\n\n // 检测更新类型\n const instance = componentRegistry.get(componentId);\n if (!instance) {\n console.warn(`[LytJS HMR] Component instance not found: ${componentId}`);\n return;\n }\n\n const oldComponent = instance.component;\n\n // 检测模板变化\n const templateChanged = oldComponent.template !== newComponent.template;\n\n // 检测 setup 变化(脚本变化)\n const setupChanged = oldComponent.setup?.toString() !== newComponent.setup?.toString();\n\n // 确定更新类型\n let updateType: HMRUpdateType;\n if (setupChanged) {\n updateType = 'script';\n } else if (templateChanged) {\n updateType = 'template';\n } else {\n updateType = 'style';\n }\n\n handleComponentUpdate(componentId, newComponent, updateType);\n };\n}\n\n/**\n * 生成 HMR 代码\n *\n * 在编译时注入到组件代码中\n */\nexport function generateHMRCode(componentId: string): string {\n return `\nif (import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (newModule && newModule.default) {\n const instance = window.__LYTJS_HMR_REGISTRY__?.get('${componentId}');\n if (instance) {\n instance.component = newModule.default;\n // 触发重新渲染\n if (instance.container && instance.component.template) {\n // Vapor 模式会自动通过 effect 重新渲染\n }\n }\n }\n });\n}\n`;\n}\n\n// ============================================================\n// 全局 HMR 注册表\n// ============================================================\n\n// 在浏览器环境中创建全局注册表\nif (typeof window !== 'undefined') {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (window as any).__LYTJS_HMR_REGISTRY__ = componentRegistry;\n}\n\n// ============================================================\n// HMR 工具函数\n// ============================================================\n\n/**\n * 检测是否支持 HMR\n */\nexport function isHMRAvailable(): boolean {\n return typeof import.meta !== 'undefined' && !!(import.meta as unknown as { hot: unknown }).hot;\n}\n\n/**\n * 手动触发组件重新渲染\n */\nexport function forceRerender(componentId: string): boolean {\n const instance = componentRegistry.get(componentId);\n if (!instance) return false;\n\n // 触发重新渲染\n emitHMRUpdate({\n type: 'template',\n componentId,\n oldComponent: instance.component,\n newComponent: instance.component,\n timestamp: Date.now(),\n });\n\n return true;\n}\n\n/**\n * 清理所有 HMR 状态\n */\nexport function clearHMRState(): void {\n componentRegistry.clear();\n hmrListeners.clear();\n componentIdCounter = 0;\n}\n","// src/vapor/vapor-app.ts\n// @lytjs/renderer - Vapor 模式应用 API\n// Vapor 是 Signal 模式的高级封装,提供 defineVaporComponent 和 createVaporApp\n\nimport { compile } from '@lytjs/compiler';\nimport { createSignalRenderer } from '../signal/signal-renderer';\nimport type { SignalRenderer } from '../signal/signal-renderer';\nimport {\n generateComponentId,\n registerComponent,\n unregisterComponent,\n isHMRAvailable,\n} from './vapor-hmr';\n\n// __DEV__ 已在 env.d.ts 中全局声明,无需重复声明\n\n// ============================================================\n// VaporContext 接口\n// ============================================================\n\n/** Vapor 组件的上下文对象 */\nexport interface VaporContext {\n attrs: Record<string, unknown>;\n slots: Record<string, () => Node>;\n emit: (event: string, ...args: unknown[]) => void;\n}\n\n// ============================================================\n// VaporComponentOptions 接口\n// ============================================================\n\n/** Prop 定义选项 */\nexport interface PropOptions {\n type?: 'string' | 'number' | 'boolean' | 'object' | 'array' | 'function';\n default?: unknown;\n required?: boolean;\n validator?: (value: unknown) => boolean;\n}\n\n/** Vapor 组件定义选项 */\nexport interface VaporComponentOptions {\n name?: string;\n props?: Record<string, PropOptions>;\n setup?: (props: Record<string, unknown>, context: VaporContext) => Record<string, unknown> | void;\n template: string;\n}\n\n// ============================================================\n// VaporComponentDefinition 接口\n// ============================================================\n\n/** Vapor 组件定义(编译后的结果) */\nexport interface VaporComponentDefinition {\n name?: string;\n props?: Record<string, PropOptions>;\n setup?: (props: Record<string, unknown>, context: VaporContext) => Record<string, unknown> | void;\n template: string;\n compiledCode?: string;\n}\n\n// ============================================================\n// VaporAppOptions 接口\n// ============================================================\n\n/** Vapor 应用配置选项 */\nexport interface VaporAppOptions {\n /** 根容器属性(传递给根组件的 props) */\n rootProps?: Record<string, unknown>;\n}\n\n// ============================================================\n// VaporApp 接口\n// ============================================================\n\n/** Vapor 应用实例 */\nexport interface VaporApp {\n mount(container: Element | string): void;\n unmount(): void;\n provide(key: string | symbol, value: unknown): void;\n component(name: string, component: VaporComponentDefinition): VaporApp;\n}\n\n// ============================================================\n// defineVaporComponent\n// ============================================================\n\n/**\n * 定义一个 Vapor 模式的组件\n *\n * 将模板编译结果缓存到闭包中,返回组件定义对象。\n *\n * @param options - 组件定义选项\n * @returns VaporComponentDefinition\n *\n * @example\n * ```ts\n * const MyComponent = defineVaporComponent({\n * name: 'MyComponent',\n * props: {\n * message: { type: String, default: 'hello' }\n * },\n * setup(props, { emit }) {\n * return { count: 0, onClick: () => emit('click') };\n * },\n * template: '<div @click=\"onClick\">{{ message }}</div>'\n * });\n * ```\n */\nexport function defineVaporComponent(options: VaporComponentOptions): VaporComponentDefinition {\n const { name, props, setup, template } = options;\n\n // 预编译模板,将编译结果缓存到闭包中\n let compiledCode: string | undefined;\n try {\n const compileResult = compile(template, { rendererMode: 'signal' });\n compiledCode = compileResult.code;\n } catch (e) {\n // FIX: P2-v11-01 生产环境编译错误不再静默吞没,\n // 在非 DEV 环境下将错误记录到 console.error,确保问题可追踪\n if (__DEV__) {\n console.warn(\n `[LytJS] defineVaporComponent: template compilation failed for \"${name || 'anonymous'}\". ` +\n `Error: ${e instanceof Error ? e.message : String(e)}`,\n );\n } else {\n console.error(\n `[LytJS] defineVaporComponent: template compilation failed for \"${name || 'anonymous'}\". ` +\n `Error: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n return {\n name,\n props,\n setup,\n template,\n compiledCode,\n };\n}\n\n// ============================================================\n// createVaporApp\n// ============================================================\n\n/**\n * 创建一个 Vapor 模式的应用实例\n *\n * 内部使用 createSignalRenderer 进行渲染。\n *\n * @param rootComponent - 根组件定义\n * @param options - 应用配置选项\n * @returns VaporApp 实例\n *\n * @example\n * ```ts\n * const App = defineVaporComponent({\n * template: '<div>{{ message }}</div>',\n * setup() {\n * return { message: 'Hello Vapor' };\n * }\n * });\n *\n * const app = createVaporApp(App);\n * app.mount('#app');\n * ```\n */\nexport function createVaporApp(\n rootComponent: VaporComponentDefinition,\n options?: VaporAppOptions,\n): VaporApp {\n const provides = new Map<string | symbol, unknown>();\n const components = new Map<string, VaporComponentDefinition>();\n\n let signalRenderer: SignalRenderer | null = null;\n let isMounted = false;\n let isUnmounted = false;\n\n // Phase 1.2: HMR 组件 ID\n const componentId = generateComponentId();\n\n const vaporApp: VaporApp = {\n mount(container: Element | string) {\n if (isUnmounted) {\n throw new Error(\n `[LytJS] VaporApp has been unmounted and cannot be remounted. ` +\n `Create a new app instance instead.`,\n );\n }\n\n if (isMounted) {\n throw new Error(\n `[LytJS] VaporApp is already mounted. Call app.unmount() first before mounting again.`,\n );\n }\n\n // 解析容器\n const el = typeof container === 'string' ? document.querySelector(container) : container;\n\n if (!el) {\n throw new Error(`[LytJS] VaporApp: cannot find element matching \"${container}\".`);\n }\n\n // 构建上下文对象\n const rootProps = options?.rootProps ?? {};\n const ctx: Record<string, unknown> = { ...rootProps };\n\n // 创建 VaporContext\n const vaporContext: VaporContext = {\n attrs: { ...rootProps },\n slots: {},\n emit(event: string, ...args: unknown[]) {\n if (__DEV__) {\n // eslint-disable-next-line no-console\n console.log(`[LytJS] VaporComponent emitted event \"${event}\"`, args);\n }\n },\n };\n\n // 执行 setup 函数\n if (typeof rootComponent.setup === 'function') {\n const setupResult = rootComponent.setup(rootProps, vaporContext);\n if (setupResult && typeof setupResult === 'object') {\n Object.assign(ctx, setupResult);\n }\n }\n\n // FIX: P2-v11-02 createVaporApp 验证 template 存在性,\n // 避免缺少 template 时在运行时产生难以调试的错误\n if (!rootComponent.template) {\n throw new Error(\n `[LytJS] createVaporApp: rootComponent must have a 'template' property. ` +\n `Received: ${typeof rootComponent.template}`,\n );\n }\n\n // 创建 Signal 渲染器\n signalRenderer = createSignalRenderer(rootComponent.template, ctx);\n signalRenderer.render(el);\n\n // Phase 1.2: 注册组件实例用于 HMR\n if (__DEV__ || isHMRAvailable()) {\n registerComponent(componentId, rootComponent, el);\n }\n\n isMounted = true;\n },\n\n unmount() {\n if (signalRenderer) {\n signalRenderer.unmount();\n signalRenderer = null;\n }\n\n // Phase 1.2: 注销组件实例\n if (__DEV__ || isHMRAvailable()) {\n unregisterComponent(componentId);\n }\n\n isMounted = false;\n isUnmounted = true;\n\n // 清理注册的资源\n provides.clear();\n components.clear();\n },\n\n provide(key: string | symbol, value: unknown): void {\n if (__DEV__ && isMounted) {\n console.warn(\n '[LytJS] VaporApp.provide() cannot be called after the app has been mounted. ' +\n 'Register provides before calling app.mount().',\n );\n }\n provides.set(key, value);\n },\n\n component(name: string, component: VaporComponentDefinition): VaporApp {\n components.set(name, component);\n return vaporApp;\n },\n };\n\n return vaporApp;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lytjs/renderer",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"description": "LytJS renderer - DOM, SSR, and Vapor rendering backends",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
"clean": "rm -rf dist"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lytjs/reactivity": "^6.
|
|
50
|
-
"@lytjs/vdom": "^6.
|
|
51
|
-
"@lytjs/common-is": "^6.
|
|
52
|
-
"@lytjs/common-string": "^6.
|
|
53
|
-
"@lytjs/common-events": "^6.
|
|
54
|
-
"@lytjs/common-dom": "^6.
|
|
55
|
-
"@lytjs/common-error": "^6.
|
|
56
|
-
"@lytjs/dom-runtime": "^6.
|
|
57
|
-
"@lytjs/compiler": "^6.
|
|
58
|
-
"@lytjs/host-contract": "^6.
|
|
59
|
-
"@lytjs/adapter-web": "^6.
|
|
49
|
+
"@lytjs/reactivity": "^6.7.0",
|
|
50
|
+
"@lytjs/vdom": "^6.7.0",
|
|
51
|
+
"@lytjs/common-is": "^6.7.0",
|
|
52
|
+
"@lytjs/common-string": "^6.7.0",
|
|
53
|
+
"@lytjs/common-events": "^6.7.0",
|
|
54
|
+
"@lytjs/common-dom": "^6.7.0",
|
|
55
|
+
"@lytjs/common-error": "^6.7.0",
|
|
56
|
+
"@lytjs/dom-runtime": "^6.7.0",
|
|
57
|
+
"@lytjs/compiler": "^6.7.0",
|
|
58
|
+
"@lytjs/host-contract": "^6.7.0",
|
|
59
|
+
"@lytjs/adapter-web": "^6.7.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"tsup": "^8.0.0",
|