native-sfc 0.0.1 → 0.0.2

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 CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  Load a single HTML file as a Web Component.
4
4
 
5
+ ```html
6
+ <!-- index.html -->
7
+ <my-counter></my-counter>
8
+ <script type="module">
9
+ import { loadComponent } from "https://esm.sh/native-sfc";
10
+ loadComponent("my-counter", "./my-counter.html");
11
+ </script>
12
+ ```
13
+
14
+ ```html
15
+ <!-- my-counter.html -->
16
+ <button @click="setCount(count() + 1)">Count is: {{ count() }}</button>
17
+ <script type="module">
18
+ import { signal } from "https://esm.sh/native-sfc";
19
+ export default class MyCounter extends HTMLElement {
20
+ setup() {
21
+ const [count, setCount] = signal(0);
22
+ return { count, setCount };
23
+ }
24
+ connectedCallback() {}
25
+ }
26
+ </script>
27
+ ```
28
+
5
29
  ## How it works
6
30
 
7
31
  The component loader fetches everything (HTML, JS, CSS) as text,
@@ -13,22 +37,107 @@ then processes them to create a Web Component.
13
37
  4. **Global Styles**: Styles with `global` attribute are moved to the outer document instead of the shadow root
14
38
  5. **URL Rewriting**: All relative URLs in `src` and `href` attributes are rewritten to absolute URLs based on the component file location
15
39
 
16
- ## API
40
+ ## Component API
17
41
 
18
- ### `loadComponent(name, url, afterConstructor?)`
42
+ ### `loadComponent(name: string, url: string)`
19
43
 
20
44
  Load a component from a HTML file and register it as a custom element.
21
45
 
22
46
  - `name`: The custom element name (e.g., `"my-component"`)
23
47
  - `url`: The URL to the component HTML file (relative to the importer)
24
- - `afterConstructor`: Optional callback executed after the component constructor
25
48
 
26
- ### `defineComponent(fc)`
49
+ ### `defineComponent(setup: ({ onConnected?, onDisconnected? }) => void)`
27
50
 
28
51
  A helper function for dual-mode component definition:
29
52
 
30
- - When used inside a `loadComponent`-imported module, it defines a web component class
31
- - When used in normal document context, it runs the function with `document` as root
53
+ - When used inside a `loadComponent`-imported module, it defines a web component class with lifecycle callbacks
54
+ - When used in normal document context, it runs the setup function with `document` as root
55
+ - The setup function receives `{ onConnected, onDisconnected }` callbacks
56
+ - Return an object from setup to expose reactive state to the template
57
+
58
+ ## Signals API
59
+
60
+ ### `signal<T>(initialValue: T): [() => T, (v: T) => void]`
61
+
62
+ Creates a reactive signal with a getter and setter.
63
+
64
+ - Returns a tuple: `[getter, setter]`
65
+ - `getter()`: Returns the current value
66
+ - `setter(value)`: Updates the signal value and triggers reactivity
67
+
68
+ ### `computed<T>(fn: () => T): () => T`
69
+
70
+ Creates a computed value that automatically tracks dependencies.
71
+
72
+ - `fn`: Function that computes and returns the value
73
+ - Returns a getter function that returns the computed result
74
+ - Automatically updates when dependencies change
75
+
76
+ ### `effect(fn: VoidFunction): VoidFunction`
77
+
78
+ Creates a reactive effect that runs whenever its dependencies change.
79
+
80
+ - `fn`: Function to execute
81
+ - Returns a cleanup function to stop the effect
82
+ - Useful for side effects and subscriptions
83
+
84
+ ### `effectScope(fn: VoidFunction): VoidFunction`
85
+
86
+ Creates an effect scope to batch multiple effects together.
87
+
88
+ - `fn`: Function containing effect definitions
89
+ - Returns a cleanup function to stop all effects in the scope
90
+ - Useful for organizing related effects
91
+
92
+ ## HTML Template API
93
+
94
+ ### .property
95
+
96
+ Binds a DOM property to a reactive expression.
97
+
98
+ ```html
99
+ <input .value="someSignal()" />
100
+ ```
101
+
102
+ ### :attribute
103
+
104
+ Binds a DOM attribute to a reactive expression.
105
+
106
+ ```html
107
+ <img :src="imageUrl()" />
108
+ ```
109
+
110
+ ### @event
111
+
112
+ Binds a DOM event to a reactive expression.
113
+
114
+ ```html
115
+ <button @click="handleClick()" />
116
+ ```
117
+
118
+ ### {{ expression }}
119
+
120
+ Embeds a reactive expression inside text content.
121
+
122
+ ```html
123
+ <p>Total: {{ total() }}</p>
124
+ ```
125
+
126
+ ### #if="condition"
127
+
128
+ Conditionally renders an element based on a reactive expression.
129
+
130
+ ```html
131
+ <div #if="isVisible()">This content is visible only if isVisible() is true.</div>
132
+ ```
133
+
134
+ ### #for="arrayExpression"
135
+
136
+ Renders a list of elements based on a reactive array expression.
137
+
138
+ ```html
139
+ <li #for="items().map(item => ({ item }))">{{ item.name }}</li>
140
+ ```
32
141
 
33
142
  ## Limitations
34
143
 
@@ -2050,32 +2050,59 @@ function o() {
2050
2050
  throw Object.assign(Error(`Parse error ${c}:${t.slice(0, n).split("\n").length}:${n - t.lastIndexOf("\n", n - 1)}`), { idx: n });
2051
2051
  }
2052
2052
 
2053
+ // src/events.ts
2054
+ var eventTarget = new EventTarget();
2055
+ function emit(eventName, detail) {
2056
+ const event = new CustomEvent(eventName, { detail });
2057
+ eventTarget.dispatchEvent(event);
2058
+ }
2059
+ function on(eventName, listener) {
2060
+ eventTarget.addEventListener(eventName, listener);
2061
+ return () => {
2062
+ eventTarget.removeEventListener(eventName, listener);
2063
+ };
2064
+ }
2065
+
2066
+ // src/config.ts
2067
+ var config = {
2068
+ fetch: globalThis.fetch,
2069
+ rewriteModule: (code, sourceUrl) => `import.meta.url=${JSON.stringify(sourceUrl)};
2070
+ ${code}`,
2071
+ on
2072
+ };
2073
+ Object.preventExtensions(config);
2074
+
2053
2075
  // src/rewriter.ts
2054
- function rewriteModule(code, sourceUrl) {
2076
+ async function rewriteModule(code, sourceUrl) {
2055
2077
  const [imports] = parse2(code);
2056
2078
  const rewritableImports = imports.filter((i2) => {
2057
2079
  const specifier = code.slice(i2.s, i2.e);
2058
- return !isBrowserUrl(specifier) && !specifier.startsWith("data:");
2080
+ return !isBrowserUrl(specifier);
2059
2081
  });
2060
2082
  for (const importEntry of rewritableImports.reverse()) {
2061
2083
  const specifier = code.slice(importEntry.s, importEntry.e);
2062
2084
  let rewritten = specifier;
2063
2085
  if (specifier.startsWith(".") || specifier.startsWith("/")) {
2064
2086
  rewritten = new URL(specifier, sourceUrl).href;
2087
+ } else if (specifier.startsWith("node:")) {
2088
+ const module = specifier.slice(5);
2089
+ rewritten = `https://raw.esm.sh/@jspm/core/nodelibs/browser/${module}.js`;
2090
+ } else if (specifier.startsWith("npm:")) {
2091
+ rewritten = `https://esm.sh/${specifier.slice(4)}`;
2065
2092
  } else {
2066
2093
  rewritten = `https://esm.sh/${specifier}`;
2067
2094
  }
2068
2095
  code = code.slice(0, importEntry.s) + rewritten + code.slice(importEntry.e);
2069
2096
  }
2070
- return `import.meta.url=${JSON.stringify(sourceUrl)};
2071
- ${code}`;
2097
+ const { rewriteModule: rewriteModule2 } = config;
2098
+ return await rewriteModule2(code, sourceUrl);
2072
2099
  }
2073
2100
  function isBrowserUrl(url) {
2074
- return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:http://") || url.startsWith("blob:https://") || url.startsWith("data:");
2101
+ return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:http://") || url.startsWith("blob:https://");
2075
2102
  }
2076
2103
  var blobMap = /* @__PURE__ */ new Map();
2077
2104
  async function esm(code, sourceUrl) {
2078
- code = rewriteModule(code, sourceUrl);
2105
+ code = await rewriteModule(code, sourceUrl);
2079
2106
  const blob = new Blob([code], { type: "text/javascript" });
2080
2107
  const blobUrl = URL.createObjectURL(blob);
2081
2108
  blobMap.set(blobUrl, sourceUrl);
@@ -2114,14 +2141,11 @@ function warn(...args) {
2114
2141
  }
2115
2142
 
2116
2143
  // src/network.ts
2117
- var fetch = globalThis.fetch;
2118
- function defineFetch(customFetch) {
2119
- fetch = customFetch;
2120
- }
2121
2144
  async function requestText(url, userFriendlySource) {
2122
2145
  return request(url, userFriendlySource).then((res) => res.text());
2123
2146
  }
2124
2147
  async function request(url, userFriendlySource) {
2148
+ const { fetch } = config;
2125
2149
  let response;
2126
2150
  try {
2127
2151
  response = await fetch(url);
@@ -2138,22 +2162,351 @@ async function request(url, userFriendlySource) {
2138
2162
  return response;
2139
2163
  }
2140
2164
 
2141
- // src/events.ts
2142
- var eventTarget = new EventTarget();
2143
- function emit(eventName, detail) {
2144
- const event = new CustomEvent(eventName, { detail });
2145
- eventTarget.dispatchEvent(event);
2165
+ // src/signals.ts
2166
+ var activeEffect = null;
2167
+ var jobQueue = [];
2168
+ var isFlushPending = false;
2169
+ function queueJob(job) {
2170
+ if (!jobQueue.includes(job)) {
2171
+ jobQueue.push(job);
2172
+ }
2173
+ if (!isFlushPending) {
2174
+ isFlushPending = true;
2175
+ Promise.resolve().then(flushJobs);
2176
+ }
2146
2177
  }
2147
- function on(eventName, listener) {
2148
- eventTarget.addEventListener(eventName, listener);
2149
- return () => {
2150
- eventTarget.removeEventListener(eventName, listener);
2178
+ function flushJobs() {
2179
+ isFlushPending = false;
2180
+ const jobs = [...jobQueue];
2181
+ jobQueue.length = 0;
2182
+ jobs.forEach((job) => job());
2183
+ }
2184
+ function cleanup(effect2) {
2185
+ effect2.deps.forEach((dep) => {
2186
+ dep.delete(effect2);
2187
+ });
2188
+ effect2.deps.clear();
2189
+ }
2190
+ function createReactiveEffect(fn, deps = /* @__PURE__ */ new Set(), options) {
2191
+ const effect2 = fn;
2192
+ effect2.deps = deps;
2193
+ effect2.options = options;
2194
+ return effect2;
2195
+ }
2196
+ var EffectScope = class {
2197
+ effects = [];
2198
+ active = true;
2199
+ run(fn) {
2200
+ if (!this.active) return;
2201
+ const prevScope = activeScope;
2202
+ activeScope = this;
2203
+ try {
2204
+ return fn();
2205
+ } finally {
2206
+ activeScope = prevScope;
2207
+ }
2208
+ }
2209
+ add(stopFn) {
2210
+ if (this.active) {
2211
+ this.effects.push(stopFn);
2212
+ } else {
2213
+ stopFn();
2214
+ }
2215
+ }
2216
+ stop() {
2217
+ if (this.active) {
2218
+ this.effects.forEach((stop) => stop());
2219
+ this.effects = [];
2220
+ this.active = false;
2221
+ }
2222
+ }
2223
+ };
2224
+ var activeScope = null;
2225
+ function effectScope(fn) {
2226
+ const scope = new EffectScope();
2227
+ if (fn) scope.run(fn);
2228
+ return () => scope.stop();
2229
+ }
2230
+ function effect(fn) {
2231
+ const effect2 = createReactiveEffect(
2232
+ () => {
2233
+ cleanup(effect2);
2234
+ const prevEffect = activeEffect;
2235
+ activeEffect = effect2;
2236
+ try {
2237
+ fn();
2238
+ } finally {
2239
+ activeEffect = prevEffect;
2240
+ }
2241
+ },
2242
+ /* @__PURE__ */ new Set(),
2243
+ { scheduler: queueJob }
2244
+ );
2245
+ effect2();
2246
+ const stop = () => {
2247
+ cleanup(effect2);
2151
2248
  };
2249
+ if (activeScope) {
2250
+ activeScope.add(stop);
2251
+ }
2252
+ return stop;
2253
+ }
2254
+ function signal(initialValue) {
2255
+ let value = initialValue;
2256
+ const subscribers = /* @__PURE__ */ new Set();
2257
+ const read = () => {
2258
+ if (activeEffect) {
2259
+ subscribers.add(activeEffect);
2260
+ activeEffect.deps.add(subscribers);
2261
+ }
2262
+ return value;
2263
+ };
2264
+ read.toString = () => {
2265
+ throw new NativeSFCError(
2266
+ `signal<<${value}>>: This is a signal reader, you MUST call it to get the value.`
2267
+ );
2268
+ };
2269
+ const write = (newValue) => {
2270
+ if (value !== newValue) {
2271
+ value = newValue;
2272
+ const effectsToRun = new Set(subscribers);
2273
+ effectsToRun.forEach((effect2) => {
2274
+ if (effect2.options?.scheduler) {
2275
+ effect2.options.scheduler(effect2);
2276
+ } else {
2277
+ effect2();
2278
+ }
2279
+ });
2280
+ }
2281
+ };
2282
+ write.toString = () => {
2283
+ throw new NativeSFCError(
2284
+ `signal<<${value}>>: This is a signal writer, you MUST call it to set the value.`
2285
+ );
2286
+ };
2287
+ return [read, write];
2288
+ }
2289
+ function computed(fn) {
2290
+ let value;
2291
+ let dirty = true;
2292
+ let isComputing = false;
2293
+ const runner = () => {
2294
+ if (!dirty) {
2295
+ dirty = true;
2296
+ trigger(subscribers);
2297
+ }
2298
+ };
2299
+ const internalEffect = createReactiveEffect(runner);
2300
+ const subscribers = /* @__PURE__ */ new Set();
2301
+ const trigger = (subs) => {
2302
+ const effectsToRun = new Set(subs);
2303
+ effectsToRun.forEach((effect2) => {
2304
+ if (effect2.options?.scheduler) {
2305
+ effect2.options.scheduler(effect2);
2306
+ } else {
2307
+ effect2();
2308
+ }
2309
+ });
2310
+ };
2311
+ const read = () => {
2312
+ if (isComputing) {
2313
+ throw new NativeSFCError(`Circular dependency detected in computed<<${value}>>`);
2314
+ }
2315
+ if (activeEffect) {
2316
+ subscribers.add(activeEffect);
2317
+ activeEffect.deps.add(subscribers);
2318
+ }
2319
+ if (dirty) {
2320
+ isComputing = true;
2321
+ const prevEffect = activeEffect;
2322
+ activeEffect = internalEffect;
2323
+ cleanup(internalEffect);
2324
+ try {
2325
+ value = fn();
2326
+ dirty = false;
2327
+ } finally {
2328
+ activeEffect = prevEffect;
2329
+ isComputing = false;
2330
+ }
2331
+ }
2332
+ return value;
2333
+ };
2334
+ read.toString = () => {
2335
+ throw new NativeSFCError(
2336
+ `computed<<${value}>>: This is a computed reader, you MUST call it to get the value.`
2337
+ );
2338
+ };
2339
+ return read;
2340
+ }
2341
+
2342
+ // src/template.ts
2343
+ function toCamelCase(str) {
2344
+ return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
2345
+ }
2346
+ function parseTextContent(text) {
2347
+ const regex = /\{\{(.+?)\}\}/g;
2348
+ const parts = [];
2349
+ let lastIndex = 0;
2350
+ let match;
2351
+ while ((match = regex.exec(text)) !== null) {
2352
+ if (match.index > lastIndex) {
2353
+ parts.push({
2354
+ type: "static",
2355
+ content: text.slice(lastIndex, match.index)
2356
+ });
2357
+ }
2358
+ parts.push({
2359
+ type: "dynamic",
2360
+ content: match[1].trim()
2361
+ });
2362
+ lastIndex = regex.lastIndex;
2363
+ }
2364
+ if (lastIndex < text.length) {
2365
+ parts.push({
2366
+ type: "static",
2367
+ content: text.slice(lastIndex)
2368
+ });
2369
+ }
2370
+ return parts;
2371
+ }
2372
+ function reactiveNodes(nodes, context) {
2373
+ const evalExpr = (expr, additionalContext = {}) => {
2374
+ const ctx = typeof context === "object" ? Object.assign({}, context, additionalContext) : additionalContext;
2375
+ const keys = Object.keys(ctx);
2376
+ const values = Object.values(ctx);
2377
+ const func = new Function(...keys, `return ${expr.trimStart()}`);
2378
+ return func(...values);
2379
+ };
2380
+ const recursive = (nodes2) => {
2381
+ for (const node of nodes2) {
2382
+ if (node.nodeType === Node.ELEMENT_NODE) {
2383
+ const element = node;
2384
+ const ifAttr = element.getAttribute("#if");
2385
+ if (ifAttr) {
2386
+ if (element.hasAttribute("#for")) {
2387
+ console.warn("Cannot use #if and #for on the same element");
2388
+ }
2389
+ const template = element.cloneNode(true);
2390
+ const parent = element.parentNode;
2391
+ const placeholder = document.createComment("if");
2392
+ parent?.replaceChild(placeholder, element);
2393
+ template.removeAttribute("#if");
2394
+ let renderedNode = null;
2395
+ let cleanup2 = null;
2396
+ effect(() => {
2397
+ const condition = evalExpr(ifAttr);
2398
+ if (condition) {
2399
+ if (!renderedNode) {
2400
+ const clone = template.cloneNode(true);
2401
+ cleanup2 = reactiveNodes([clone], context);
2402
+ placeholder.parentNode?.insertBefore(clone, placeholder.nextSibling);
2403
+ renderedNode = clone;
2404
+ }
2405
+ } else {
2406
+ if (renderedNode) {
2407
+ cleanup2?.();
2408
+ renderedNode.remove();
2409
+ renderedNode = null;
2410
+ cleanup2 = null;
2411
+ }
2412
+ }
2413
+ });
2414
+ continue;
2415
+ }
2416
+ const forAttr = element.getAttribute("#for");
2417
+ if (forAttr) {
2418
+ const template = element.cloneNode(true);
2419
+ const parent = element.parentNode;
2420
+ const placeholder = document.createComment("for");
2421
+ parent?.replaceChild(placeholder, element);
2422
+ template.removeAttribute("#for");
2423
+ let renderedItems = [];
2424
+ effect(() => {
2425
+ const contexts = evalExpr(forAttr);
2426
+ if (!Array.isArray(contexts)) {
2427
+ console.warn("#for expression must return an array");
2428
+ return;
2429
+ }
2430
+ renderedItems.forEach(({ node: node2, cleanup: cleanup2 }) => {
2431
+ cleanup2();
2432
+ node2.remove();
2433
+ });
2434
+ renderedItems = [];
2435
+ contexts.forEach((itemContext) => {
2436
+ const clone = template.cloneNode(true);
2437
+ const cleanup2 = reactiveNodes([clone], { ...context, ...itemContext });
2438
+ placeholder.parentNode?.insertBefore(clone, placeholder.nextSibling);
2439
+ renderedItems.push({ node: clone, cleanup: cleanup2 });
2440
+ });
2441
+ });
2442
+ continue;
2443
+ }
2444
+ for (const attr of Array.from(element.attributes)) {
2445
+ if (attr.name.startsWith(".")) {
2446
+ const propName = toCamelCase(attr.name.slice(1));
2447
+ const expr = attr.value;
2448
+ effect(() => {
2449
+ const value = evalExpr(expr);
2450
+ Reflect.set(element, propName, value);
2451
+ });
2452
+ element.removeAttribute(attr.name);
2453
+ } else if (attr.name.startsWith(":")) {
2454
+ const attrName = attr.name.slice(1);
2455
+ const expr = attr.value;
2456
+ effect(() => {
2457
+ const value = evalExpr(expr);
2458
+ element.setAttribute(attrName, value);
2459
+ });
2460
+ element.removeAttribute(attr.name);
2461
+ } else if (attr.name.startsWith("@")) {
2462
+ const eventName = attr.name.slice(1);
2463
+ const expr = attr.value;
2464
+ const listener = computed(() => (event) => {
2465
+ evalExpr(expr, { event });
2466
+ })();
2467
+ element.addEventListener(eventName, listener);
2468
+ element.removeAttribute(attr.name);
2469
+ }
2470
+ }
2471
+ } else if (node.nodeType === Node.TEXT_NODE) {
2472
+ const textNode = node;
2473
+ const text = textNode.textContent || "";
2474
+ const parts = parseTextContent(text);
2475
+ if (parts.some((part) => part.type === "dynamic")) {
2476
+ const parentNode = textNode.parentNode;
2477
+ if (parentNode) {
2478
+ const fragment = document.createDocumentFragment();
2479
+ const textNodes = [];
2480
+ for (const part of parts) {
2481
+ const newTextNode = document.createTextNode(
2482
+ part.type === "static" ? part.content : ""
2483
+ );
2484
+ fragment.appendChild(newTextNode);
2485
+ textNodes.push(newTextNode);
2486
+ if (part.type === "dynamic") {
2487
+ effect(() => {
2488
+ const value = evalExpr(part.content);
2489
+ newTextNode.textContent = String(value);
2490
+ });
2491
+ }
2492
+ }
2493
+ parentNode.replaceChild(fragment, textNode);
2494
+ }
2495
+ }
2496
+ }
2497
+ if (node.childNodes.length > 0) {
2498
+ recursive(node.childNodes);
2499
+ }
2500
+ }
2501
+ };
2502
+ return effectScope(() => {
2503
+ recursive(nodes);
2504
+ });
2152
2505
  }
2153
2506
 
2154
2507
  // src/components.ts
2155
2508
  var loadedComponentsRecord = /* @__PURE__ */ new Map();
2156
- async function loadComponent(name, url, afterConstructor) {
2509
+ async function loadComponent(name, url) {
2157
2510
  const importerUrl = getImporterUrl() || location.href;
2158
2511
  url = new URL(url, importerUrl).href;
2159
2512
  emit("component-loading", { name, url });
@@ -2187,11 +2540,11 @@ async function loadComponent(name, url, afterConstructor) {
2187
2540
  );
2188
2541
  }
2189
2542
  const define = (component2) => {
2190
- const cec = extendsElement(component2, doc.body.innerHTML, adoptedStyleSheets, afterConstructor);
2191
- customElements.define(name, cec);
2543
+ const CEC = extendsElement(component2, doc.body.innerHTML, adoptedStyleSheets);
2544
+ customElements.define(name, CEC);
2192
2545
  emit("component-defined", { name, url });
2193
- loadedComponentsRecord.set(name, { cec, url });
2194
- return cec;
2546
+ loadedComponentsRecord.set(name, { cec: CEC, url });
2547
+ return CEC;
2195
2548
  };
2196
2549
  if (!component || !defaultExportIsComponent) {
2197
2550
  return define(HTMLElement);
@@ -2199,7 +2552,7 @@ async function loadComponent(name, url, afterConstructor) {
2199
2552
  return define(component);
2200
2553
  }
2201
2554
  }
2202
- function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets, afterConstructor) {
2555
+ function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets) {
2203
2556
  return class extends BaseClass {
2204
2557
  constructor(...args) {
2205
2558
  super(innerHTML, adoptedStyleSheets);
@@ -2210,22 +2563,42 @@ function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets,
2210
2563
  shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
2211
2564
  }
2212
2565
  }
2213
- if (afterConstructor) {
2214
- afterConstructor.call(this);
2566
+ if ("setup" in this && typeof this.setup === "function") {
2567
+ const context = this.setup();
2568
+ reactiveNodes(this.shadowRoot.childNodes, context);
2569
+ } else {
2570
+ reactiveNodes(this.shadowRoot.childNodes, {});
2215
2571
  }
2216
2572
  }
2217
2573
  };
2218
2574
  }
2219
- function defineComponent(fc) {
2575
+ function defineComponent(setup) {
2220
2576
  const whoDefineMe = parse(new Error().stack).at(-1).file;
2221
2577
  if (blobMap.has(whoDefineMe)) {
2222
2578
  return class extends HTMLElement {
2579
+ _onConnectedEvents = [];
2580
+ _onDisconnectedEvents = [];
2581
+ setup() {
2582
+ return setup({
2583
+ onConnected: (event) => this._onConnectedEvents.push(event),
2584
+ onDisconnected: (event) => this._onDisconnectedEvents.push(event)
2585
+ });
2586
+ }
2223
2587
  connectedCallback() {
2224
- fc.call(this, this.shadowRoot || this.attachShadow({ mode: "open" }));
2588
+ const root = this.shadowRoot || this.attachShadow({ mode: "open" });
2589
+ this._onConnectedEvents.forEach((cb) => cb.call(void 0, root));
2590
+ }
2591
+ disconnectedCallback() {
2592
+ const root = this.shadowRoot;
2593
+ this._onDisconnectedEvents.forEach((cb) => cb.call(void 0, root));
2225
2594
  }
2226
2595
  };
2227
2596
  }
2228
- return fc.call(globalThis, document);
2597
+ return setup({
2598
+ onConnected: (cb) => cb(document),
2599
+ onDisconnected: () => {
2600
+ }
2601
+ });
2229
2602
  }
2230
2603
  function filterGlobalStyle(doc) {
2231
2604
  for (const styleElement of doc.querySelectorAll("style")) {
@@ -2308,10 +2681,13 @@ async function evaluateModules(doc, url) {
2308
2681
  }
2309
2682
  export {
2310
2683
  NativeSFCError,
2684
+ computed,
2685
+ config,
2311
2686
  defineComponent,
2312
- defineFetch,
2687
+ effect,
2688
+ effectScope,
2313
2689
  loadComponent,
2314
- on
2690
+ signal
2315
2691
  };
2316
2692
  //! we provide an extra argument to user's component constructor
2317
2693
  //! if the user's constructor does not create a shadow root, we will create one here