@estjs/template 0.0.16-beta.9 → 0.0.17-beta.1

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.
@@ -1,6 +1,6 @@
1
1
  import { Signal, Computed } from '@estjs/signals';
2
- import { S as Scope, I as InjectionKey } from './internal-DR5pJGCO.js';
3
- export { i as inject, p as provide } from './internal-DR5pJGCO.js';
2
+ import { S as Scope } from './internal-DR5pJGCO.js';
3
+ export { I as InjectionKey, i as inject, p as provide } from './internal-DR5pJGCO.js';
4
4
  import { normalizeClassName } from '@estjs/shared';
5
5
 
6
6
  declare enum COMPONENT_STATE {
@@ -27,6 +27,17 @@ declare enum COMPONENT_TYPE {
27
27
  TRANSITION_GROUP = "transition-group"
28
28
  }
29
29
 
30
+ type AnyNode = Node | Component<any> | Element | string | number | boolean | null | undefined | AnyNode[] | (() => AnyNode) | Signal<AnyNode> | Computed<AnyNode>;
31
+ type ComponentProps = Record<string, unknown>;
32
+ type ComponentFn<P = ComponentProps> = (props: P) => AnyNode;
33
+ /** A mounted application instance returned by createApp() and hydrate(). */
34
+ interface AppInstance {
35
+ /** The root Component wrapper (undefined if mounting produced raw nodes). */
36
+ root: Component | undefined;
37
+ /** Tear down the application: dispose scopes, remove DOM nodes. */
38
+ unmount: () => void;
39
+ }
40
+
30
41
  declare class Component<P extends ComponentProps = {}> {
31
42
  readonly component: ComponentFn<P>;
32
43
  props: P;
@@ -90,112 +101,56 @@ declare function isComponent(node: unknown): node is Component;
90
101
  */
91
102
  declare function createComponent<P extends ComponentProps>(componentFn: ComponentFn<P>, props?: P): Component<P>;
92
103
 
93
- type AnyNode = Node | Component<any> | Element | string | number | boolean | null | undefined | AnyNode[] | (() => AnyNode) | Signal<AnyNode> | Computed<AnyNode>;
94
- type ComponentProps = Record<string, unknown>;
95
- type ComponentFn<P = ComponentProps> = (props: P) => AnyNode;
96
- /** Phase + plugin name passed to error/warn handlers. */
97
- interface ErrorInfo {
98
- phase: 'install' | 'mount' | 'cleanup' | 'effect';
99
- plugin?: string;
100
- }
101
- /**
102
- * Application-level configuration. Plugins and user code can set these to
103
- * customize framework behaviour without monkey-patching internals.
104
- */
105
- interface AppConfig {
106
- /** Invoked when an uncaught error propagates out of a plugin / lifecycle hook / effect. */
107
- errorHandler?: (info: ErrorInfo, error: unknown) => void;
108
- /** Custom warn handler (dev only). When set, framework `warn()` calls are redirected here. */
109
- warnHandler?: (info: {
110
- plugin?: string;
111
- }, message: string) => void;
112
- }
113
- /**
114
- * Context passed to every plugin's setup function. Plugins use this to
115
- * register providers, hook lifecycle events, and report problems.
116
- */
117
- interface AppContext {
118
- /** Provide a value at the application root scope, retrievable via `inject()`. */
119
- provide<T>(key: InjectionKey<T> | string | number, value: T): void;
120
- /** Inject a value from the application scope. Plugins ordered later can read what earlier plugins provided. */
121
- inject<T>(key: InjectionKey<T> | string | number, defaultValue?: T): T;
122
- /** Register a callback that runs after the root component mounts. */
123
- onMount(fn: () => void): void;
124
- /** Register a callback that runs when the application is unmounted. */
125
- onCleanup(fn: () => void): void;
126
- /** Non-fatal report. Routed to config.warnHandler if set, otherwise a dev console warning. */
127
- warn(message: string): void;
128
- /** Fatal report. Throws — plugin setup fails, mount() rejects. */
129
- error(message: string): never;
130
- /** Application config — readable and mutable by plugins. */
131
- config: AppConfig;
132
- /** Framework version (injected by the build; empty string in unbundled tests). */
133
- version: string;
134
- }
135
104
  /**
136
- * Plugin definition.
105
+ * Create a template factory function from HTML string.
106
+ *
107
+ * This function creates a reusable template factory that efficiently clones
108
+ * DOM nodes from the provided HTML string. The template is parsed once.
109
+ *
110
+ * Security note: `template(html)` is a raw HTML entrypoint. The caller is
111
+ * responsible for ensuring `html` is trusted and not derived from unsanitized
112
+ * user input.
113
+ *
114
+ * @param html - The HTML string to create template from.
115
+ * @returns Factory function that returns a cloned node of the template.
116
+ * @throws {Error} When template content is empty or invalid.
137
117
  *
138
118
  * @example
139
- * ```ts
140
- * const router = definePlugin<{ routes: Route[] }>({
141
- * name: 'essor:router',
142
- * enforce: 'pre',
143
- * setup(ctx, options) {
144
- * ctx.provide(RouterKey, createRouter(options.routes));
145
- * },
146
- * });
119
+ * ```typescript
120
+ * const buttonTemplate = template('<button>Click me</button>');
121
+ * const button1 = buttonTemplate(); // Creates first button instance
122
+ * const button2 = buttonTemplate(); // Creates second button instance
147
123
  * ```
148
124
  */
149
- interface Plugin<TOptions = void> {
150
- /** Required. Used for dedup, error messages, ordering. */
151
- name: string;
152
- /** Coarse ordering bucket. Default 'default'. Order: pre → default → post; within bucket, array order wins. */
153
- enforce?: 'pre' | 'default' | 'post';
154
- /** Setup. May be async; mount() awaits it. */
155
- setup(ctx: AppContext, options: TOptions): void | Promise<void>;
156
- }
157
- /** Plugin entry in `CreateAppOptions.plugins` — bare or paired with options. */
158
- type PluginEntry = Plugin<void> | readonly [Plugin<any>, unknown];
159
- /** Options accepted by the config-object form of `createApp(component, options)`. */
160
- interface CreateAppOptions {
161
- plugins?: ReadonlyArray<PluginEntry>;
162
- config?: Partial<AppConfig>;
163
- }
125
+ declare function template(html: string): () => Node;
164
126
  /**
165
- * Application builder returned by `createApp(component)` or `createApp(component, options)`.
127
+ * Create and mount an application with the specified component.
128
+ *
129
+ * This function initializes an application by mounting a root component
130
+ * to a target DOM element. It handles target validation and cleanup.
131
+ *
132
+ * @param component - The root component function to mount.
133
+ * @param target - CSS selector string or DOM element to mount to.
134
+ * @returns Object with root component and unmount function.
166
135
  *
167
136
  * @example
168
- * ```ts
169
- * await createApp(App, {
170
- * plugins: [router, [store, { initial: {} }]],
171
- * }).mount('#root');
137
+ * ```typescript
138
+ * const App = () => template('<div>Hello World</div>')
139
+ * const app = createApp(App, '#root');
140
+ *
141
+ * // Or with DOM element
142
+ * const container = document.getElementById('app');
143
+ * const app = createApp(App, container);
172
144
  * ```
173
145
  */
174
- interface App {
175
- /** Application config. Mutate before mounting to install error/warn handlers. */
176
- config: AppConfig;
177
- /** Mount into the DOM. Returns a Promise when any plugin has async setup; sync otherwise. */
178
- mount(target: string | Element): Promise<AppInstance | undefined> | AppInstance | undefined;
179
- /** Hydrate over SSR HTML. Returns a Promise when any plugin has async setup; sync otherwise. */
180
- hydrate(target: string | Element): Promise<AppInstance | undefined> | AppInstance | undefined;
181
- }
182
- /** A mounted application instance. */
183
- interface AppInstance {
184
- /** The root Component wrapper (undefined if mounting produced raw nodes). */
185
- root: Component | undefined;
186
- /** Tear down the application: dispose scopes, remove DOM nodes. */
146
+ declare function createApp<P extends ComponentProps = {}>(component: ComponentFn<P>, target: string | Element): {
147
+ root: Component<{}> | undefined;
187
148
  unmount: () => void;
188
- }
189
-
190
- /**
191
- * Create a reusable template factory from an HTML string. Parsed once, cloned per call.
192
- * Caller is responsible for ensuring `html` is trusted.
193
- */
194
- declare function template(html: string): () => Node;
195
- declare function createApp<P extends ComponentProps = {}>(component: ComponentFn<P>): App;
196
- declare function createApp<P extends ComponentProps = {}, A extends string | Element | CreateAppOptions = string | Element | CreateAppOptions>(component: ComponentFn<P>, arg: A): A extends string | Element ? AppInstance | undefined : App;
197
- declare function hydrate<P extends ComponentProps = {}>(component: ComponentFn<P>, target: string | Element): AppInstance | undefined | Promise<AppInstance | undefined>;
198
- declare function definePlugin<T = void>(plugin: Plugin<T>): Plugin<T>;
149
+ } | undefined;
150
+ declare function hydrate<P extends ComponentProps = {}>(component: ComponentFn<P>, target: string | Element): {
151
+ root: Component<{}> | undefined;
152
+ unmount: () => void;
153
+ } | undefined;
199
154
 
200
155
  type LifecycleHook = () => void | Promise<void>;
201
156
  /**
@@ -817,4 +772,4 @@ declare function TransitionGroup<T>(props: TransitionGroupProps<T>): Element;
817
772
  */
818
773
  declare function isTransitionGroup(node: unknown): boolean;
819
774
 
820
- export { type App, type AppConfig, type AppContext, type AppInstance, type AsyncComponentOptions, Component, type ComponentFn, type ComponentProps, type CreateAppOptions, type ErrorInfo, For, Fragment, InjectionKey, type Plugin, type PluginEntry, Portal, Suspense, Transition, TransitionGroup, type TransitionGroupProps, type TransitionProps, addEvent, addEventListener, beginHydration, bindElement, child, clearDelegatedEvents, consumeTeleportAnchor, consumeTeleportBlock, createApp, createComponent, createResource, defineAsyncComponent, definePlugin, delegateEvents, endHydration, getHydrationKey, getRenderedElement, hydrate, hydrationAnchor, hydrationMarker, insert, isComponent, isFragment, isHydrating, isPortal, isSuspense, isTransition, isTransitionGroup, next, normalizeClass, nthChild, omitProps, onDestroy, onMount, onUpdate, patchAttr, patchAttrHydrate, patchClass, patchClassHydrate, patchStyle, patchStyleHydrate, resetHydrationKey, setStyle, template };
775
+ export { type AppInstance, type AsyncComponentOptions, Component, type ComponentFn, type ComponentProps, For, Fragment, Portal, Suspense, Transition, TransitionGroup, type TransitionGroupProps, type TransitionProps, addEvent, addEventListener, beginHydration, bindElement, child, clearDelegatedEvents, consumeTeleportAnchor, consumeTeleportBlock, createApp, createComponent, createResource, defineAsyncComponent, delegateEvents, endHydration, getHydrationKey, getRenderedElement, hydrate, hydrationAnchor, hydrationMarker, insert, isComponent, isFragment, isHydrating, isPortal, isSuspense, isTransition, isTransitionGroup, next, normalizeClass, nthChild, omitProps, onDestroy, onMount, onUpdate, patchAttr, patchAttrHydrate, patchClass, patchClassHydrate, patchStyle, patchStyleHydrate, resetHydrationKey, setStyle, template };
@@ -3,22 +3,9 @@
3
3
  var shared = require('@estjs/shared');
4
4
  var signals = require('@estjs/signals');
5
5
 
6
- var __defProp = Object.defineProperty;
7
6
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
8
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
- var __spreadValues = (a, b) => {
12
- for (var prop in b || (b = {}))
13
- if (__hasOwnProp.call(b, prop))
14
- __defNormalProp(a, prop, b[prop]);
15
- if (__getOwnPropSymbols)
16
- for (var prop of __getOwnPropSymbols(b)) {
17
- if (__propIsEnum.call(b, prop))
18
- __defNormalProp(a, prop, b[prop]);
19
- }
20
- return a;
21
- };
22
9
  var __objRest = (source, exclude) => {
23
10
  var target = {};
24
11
  for (var prop in source)
@@ -1169,6 +1156,91 @@ function createComponent(componentFn, props) {
1169
1156
  }
1170
1157
  return new Component(componentFn, props);
1171
1158
  }
1159
+
1160
+ // src/renderer.ts
1161
+ function template(html) {
1162
+ let node;
1163
+ const create = () => {
1164
+ const template2 = document.createElement("template");
1165
+ template2.innerHTML = html;
1166
+ const firstChild = template2.content.firstChild;
1167
+ if (!firstChild) {
1168
+ throw new Error("Invalid template: empty content");
1169
+ }
1170
+ return firstChild;
1171
+ };
1172
+ return () => (node || (node = create())).cloneNode(true);
1173
+ }
1174
+ function createApp(component, target) {
1175
+ const container = shared.isString(target) ? document.querySelector(target) : target;
1176
+ if (!container) {
1177
+ {
1178
+ shared.warn(`Target element not found: ${target}`);
1179
+ }
1180
+ return;
1181
+ }
1182
+ const existingContent = container.innerHTML;
1183
+ if (existingContent) {
1184
+ {
1185
+ shared.warn(`Target element is not empty, it will be cleared: ${target}`);
1186
+ }
1187
+ container.innerHTML = "";
1188
+ }
1189
+ const scope = createScope();
1190
+ let rootNode;
1191
+ try {
1192
+ runWithScope(scope, () => {
1193
+ const mountedRoot = createComponent(component);
1194
+ if (isComponent(mountedRoot)) {
1195
+ rootNode = mountedRoot;
1196
+ insertNode(container, mountedRoot);
1197
+ }
1198
+ });
1199
+ } catch (error_) {
1200
+ disposeScope(scope);
1201
+ throw error_;
1202
+ }
1203
+ return {
1204
+ root: rootNode,
1205
+ unmount: () => {
1206
+ disposeScope(scope);
1207
+ rootNode == null ? void 0 : rootNode.destroy();
1208
+ }
1209
+ };
1210
+ }
1211
+ function hydrate(component, target) {
1212
+ const container = shared.isString(target) ? document.querySelector(target) : target;
1213
+ if (!container) {
1214
+ {
1215
+ shared.warn(`[essor] hydrate: target element not found: ${target}`);
1216
+ }
1217
+ return;
1218
+ }
1219
+ beginHydration(container);
1220
+ const scope = createScope();
1221
+ let rootNode;
1222
+ try {
1223
+ runWithScope(scope, () => {
1224
+ const mountedRoot = createComponent(component);
1225
+ if (isComponent(mountedRoot)) {
1226
+ rootNode = mountedRoot;
1227
+ insert(container, mountedRoot);
1228
+ }
1229
+ });
1230
+ } catch (error_) {
1231
+ disposeScope(scope);
1232
+ throw error_;
1233
+ } finally {
1234
+ endHydration();
1235
+ }
1236
+ return {
1237
+ root: rootNode,
1238
+ unmount: () => {
1239
+ disposeScope(scope);
1240
+ rootNode == null ? void 0 : rootNode.destroy();
1241
+ }
1242
+ };
1243
+ }
1172
1244
  function provide(key, value) {
1173
1245
  const scope = getActiveScope();
1174
1246
  if (!scope) {
@@ -1201,186 +1273,6 @@ function inject(key, defaultValue) {
1201
1273
  }
1202
1274
  return defaultValue;
1203
1275
  }
1204
-
1205
- // src/renderer.ts
1206
- var VERSION = typeof __VERSION__ === "string" ? __VERSION__ : "";
1207
- var ENFORCE_ORDER = { pre: 0, default: 1, post: 2 };
1208
- function template(html) {
1209
- let node;
1210
- const create = () => {
1211
- const template2 = document.createElement("template");
1212
- template2.innerHTML = html;
1213
- const firstChild = template2.content.firstChild;
1214
- if (!firstChild) {
1215
- throw new Error("Invalid template: empty content");
1216
- }
1217
- return firstChild;
1218
- };
1219
- return () => (node || (node = create())).cloneNode(true);
1220
- }
1221
- function createApp(component, arg) {
1222
- if (shared.isString(arg) || arg instanceof Element) {
1223
- return buildApp(component).mount(arg);
1224
- }
1225
- return buildApp(component, arg);
1226
- }
1227
- function hydrate(component, target) {
1228
- return buildApp(component).hydrate(target);
1229
- }
1230
- function definePlugin(plugin) {
1231
- return plugin;
1232
- }
1233
- function buildApp(component, options) {
1234
- var _a2, _b;
1235
- const plugins = (_a2 = options == null ? void 0 : options.plugins) != null ? _a2 : [];
1236
- const config = __spreadValues({}, (_b = options == null ? void 0 : options.config) != null ? _b : {});
1237
- let mounted = false;
1238
- function mount(target, isHydrate) {
1239
- if (mounted) {
1240
- shared.warn("App is already mounted");
1241
- return;
1242
- }
1243
- mounted = true;
1244
- const container = shared.isString(target) ? document.querySelector(target) : target;
1245
- if (!container) {
1246
- {
1247
- shared.warn(`[essor] ${isHydrate ? "hydrate" : "createApp"}: target element not found: ${target}`);
1248
- }
1249
- return;
1250
- }
1251
- if (!isHydrate && container.innerHTML) {
1252
- shared.warn(`Target element is not empty, it will be cleared: ${target}`);
1253
- container.innerHTML = "";
1254
- }
1255
- const scope = createScope();
1256
- const mountHooks = [];
1257
- const seenNames = /* @__PURE__ */ new Set();
1258
- const seenRefs = /* @__PURE__ */ new Set();
1259
- let activePluginName;
1260
- let root;
1261
- function reportError(error5, info) {
1262
- if (config.errorHandler) config.errorHandler(info, error5);
1263
- else throw error5;
1264
- }
1265
- const ordered = plugins.map((entry, index) => ({ entry, index })).sort((a, b) => {
1266
- var _a3, _b2;
1267
- const pa = ENFORCE_ORDER[(_a3 = normalizePlugin(a.entry).enforce) != null ? _a3 : "default"];
1268
- const pb = ENFORCE_ORDER[(_b2 = normalizePlugin(b.entry).enforce) != null ? _b2 : "default"];
1269
- return pa - pb || a.index - b.index;
1270
- }).map((x) => x.entry);
1271
- function runSetup(plugin, opts) {
1272
- if (seenRefs.has(plugin) || seenNames.has(plugin.name)) {
1273
- shared.warn(`Plugin "${plugin.name}" is already registered, skipping`);
1274
- return;
1275
- }
1276
- seenRefs.add(plugin);
1277
- seenNames.add(plugin.name);
1278
- activePluginName = plugin.name;
1279
- try {
1280
- const result = plugin.setup(ctx, opts);
1281
- if (result instanceof Promise) {
1282
- const pinned = plugin.name;
1283
- return result.catch((error5) => reportError(error5, { phase: "install", plugin: pinned })).finally(() => {
1284
- if (activePluginName === pinned) activePluginName = void 0;
1285
- });
1286
- }
1287
- activePluginName = void 0;
1288
- return result;
1289
- } catch (error5) {
1290
- activePluginName = void 0;
1291
- reportError(error5, { phase: "install", plugin: plugin.name });
1292
- }
1293
- }
1294
- const ctx = {
1295
- provide,
1296
- inject,
1297
- onMount: (fn) => void mountHooks.push(fn),
1298
- onCleanup,
1299
- warn(message) {
1300
- const info = { plugin: activePluginName };
1301
- if (config.warnHandler) config.warnHandler(info, message);
1302
- else shared.warn(message);
1303
- },
1304
- error(message) {
1305
- throw new Error(message);
1306
- },
1307
- config,
1308
- version: VERSION
1309
- };
1310
- function finishMount() {
1311
- if (isHydrate) beginHydration(container);
1312
- try {
1313
- const node = createComponent(component);
1314
- if (isComponent(node)) {
1315
- root = node;
1316
- (isHydrate ? insert : insertNode)(container, node);
1317
- }
1318
- } finally {
1319
- if (isHydrate) endHydration();
1320
- }
1321
- for (const fn of mountHooks) {
1322
- try {
1323
- fn();
1324
- } catch (error5) {
1325
- reportError(error5, { phase: "mount" });
1326
- }
1327
- }
1328
- return {
1329
- root,
1330
- unmount() {
1331
- disposeScope(scope);
1332
- root == null ? void 0 : root.destroy();
1333
- }
1334
- };
1335
- }
1336
- let asyncTail;
1337
- try {
1338
- runWithScope(scope, () => {
1339
- for (const entry of ordered) {
1340
- const [plugin, opts] = unpack(entry);
1341
- if (asyncTail) {
1342
- asyncTail = asyncTail.then(
1343
- () => runWithScope(scope, () => {
1344
- const r = runSetup(plugin, opts);
1345
- return r instanceof Promise ? r : void 0;
1346
- })
1347
- );
1348
- continue;
1349
- }
1350
- const result = runSetup(plugin, opts);
1351
- if (result instanceof Promise) asyncTail = result;
1352
- }
1353
- });
1354
- } catch (error5) {
1355
- disposeScope(scope);
1356
- throw error5;
1357
- }
1358
- if (asyncTail) {
1359
- return asyncTail.then(() => runWithScope(scope, finishMount)).catch((error5) => {
1360
- disposeScope(scope);
1361
- throw error5;
1362
- });
1363
- }
1364
- try {
1365
- return runWithScope(scope, finishMount);
1366
- } catch (error5) {
1367
- disposeScope(scope);
1368
- throw error5;
1369
- }
1370
- }
1371
- const app = {
1372
- config,
1373
- mount: (target) => mount(target, false),
1374
- hydrate: (target) => mount(target, true)
1375
- };
1376
- return app;
1377
- }
1378
- function normalizePlugin(entry) {
1379
- return Array.isArray(entry) ? entry[0] : entry;
1380
- }
1381
- function unpack(entry) {
1382
- return Array.isArray(entry) ? [entry[0], entry[1]] : [entry, void 0];
1383
- }
1384
1276
  function reTargetEvent(e, value) {
1385
1277
  Object.defineProperty(e, "target", {
1386
1278
  configurable: true,
@@ -3048,7 +2940,6 @@ exports.createApp = createApp;
3048
2940
  exports.createComponent = createComponent;
3049
2941
  exports.createResource = createResource;
3050
2942
  exports.defineAsyncComponent = defineAsyncComponent;
3051
- exports.definePlugin = definePlugin;
3052
2943
  exports.delegateEvents = delegateEvents;
3053
2944
  exports.endHydration = endHydration;
3054
2945
  exports.getHydrationKey = getHydrationKey;