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 +115 -6
- package/dist/index.bundled.js +408 -32
- package/dist/index.cdn.js +416 -38
- package/dist/index.d.ts +61 -12
- package/dist/index.js +416 -38
- package/package.json +3 -3
package/dist/index.cdn.js
CHANGED
|
@@ -1,36 +1,65 @@
|
|
|
1
1
|
// dependencies loaded from esm.sh
|
|
2
2
|
|
|
3
3
|
// src/components.ts
|
|
4
|
-
import * as
|
|
4
|
+
import * as stackTraceParser from "https://esm.sh/stacktrace-parser";
|
|
5
5
|
|
|
6
6
|
// src/rewriter.ts
|
|
7
|
-
import { parse } from "https://esm.sh/es-module-lexer/js";
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
import { parse as parseESM } from "https://esm.sh/es-module-lexer/js";
|
|
8
|
+
import { parse as parseStackTrace } from "https://esm.sh/stacktrace-parser";
|
|
9
|
+
|
|
10
|
+
// src/events.ts
|
|
11
|
+
var eventTarget = new EventTarget();
|
|
12
|
+
function emit(eventName, detail) {
|
|
13
|
+
const event = new CustomEvent(eventName, { detail });
|
|
14
|
+
eventTarget.dispatchEvent(event);
|
|
15
|
+
}
|
|
16
|
+
function on(eventName, listener) {
|
|
17
|
+
eventTarget.addEventListener(eventName, listener);
|
|
18
|
+
return () => {
|
|
19
|
+
eventTarget.removeEventListener(eventName, listener);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/config.ts
|
|
24
|
+
var config = {
|
|
25
|
+
fetch: globalThis.fetch,
|
|
26
|
+
rewriteModule: (code, sourceUrl) => `import.meta.url=${JSON.stringify(sourceUrl)};
|
|
27
|
+
${code}`,
|
|
28
|
+
on
|
|
29
|
+
};
|
|
30
|
+
Object.preventExtensions(config);
|
|
31
|
+
|
|
32
|
+
// src/rewriter.ts
|
|
33
|
+
async function rewriteModule(code, sourceUrl) {
|
|
34
|
+
const [imports] = parseESM(code);
|
|
11
35
|
const rewritableImports = imports.filter((i) => {
|
|
12
36
|
const specifier = code.slice(i.s, i.e);
|
|
13
|
-
return !isBrowserUrl(specifier)
|
|
37
|
+
return !isBrowserUrl(specifier);
|
|
14
38
|
});
|
|
15
39
|
for (const importEntry of rewritableImports.reverse()) {
|
|
16
40
|
const specifier = code.slice(importEntry.s, importEntry.e);
|
|
17
41
|
let rewritten = specifier;
|
|
18
42
|
if (specifier.startsWith(".") || specifier.startsWith("/")) {
|
|
19
43
|
rewritten = new URL(specifier, sourceUrl).href;
|
|
44
|
+
} else if (specifier.startsWith("node:")) {
|
|
45
|
+
const module = specifier.slice(5);
|
|
46
|
+
rewritten = `https://raw.esm.sh/@jspm/core/nodelibs/browser/${module}.js`;
|
|
47
|
+
} else if (specifier.startsWith("npm:")) {
|
|
48
|
+
rewritten = `https://esm.sh/${specifier.slice(4)}`;
|
|
20
49
|
} else {
|
|
21
50
|
rewritten = `https://esm.sh/${specifier}`;
|
|
22
51
|
}
|
|
23
52
|
code = code.slice(0, importEntry.s) + rewritten + code.slice(importEntry.e);
|
|
24
53
|
}
|
|
25
|
-
|
|
26
|
-
|
|
54
|
+
const { rewriteModule: rewriteModule2 } = config;
|
|
55
|
+
return await rewriteModule2(code, sourceUrl);
|
|
27
56
|
}
|
|
28
57
|
function isBrowserUrl(url) {
|
|
29
|
-
return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:http://") || url.startsWith("blob:https://")
|
|
58
|
+
return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("blob:http://") || url.startsWith("blob:https://");
|
|
30
59
|
}
|
|
31
60
|
var blobMap = /* @__PURE__ */ new Map();
|
|
32
61
|
async function esm(code, sourceUrl) {
|
|
33
|
-
code = rewriteModule(code, sourceUrl);
|
|
62
|
+
code = await rewriteModule(code, sourceUrl);
|
|
34
63
|
const blob = new Blob([code], { type: "text/javascript" });
|
|
35
64
|
const blobUrl = URL.createObjectURL(blob);
|
|
36
65
|
blobMap.set(blobUrl, sourceUrl);
|
|
@@ -42,7 +71,7 @@ async function esm(code, sourceUrl) {
|
|
|
42
71
|
}
|
|
43
72
|
}
|
|
44
73
|
function getImporterUrl() {
|
|
45
|
-
const stack =
|
|
74
|
+
const stack = parseStackTrace(new Error().stack);
|
|
46
75
|
for (const { file } of stack) {
|
|
47
76
|
if (file && file !== import.meta.url) {
|
|
48
77
|
if (file.startsWith("blob:")) {
|
|
@@ -69,14 +98,11 @@ function warn(...args) {
|
|
|
69
98
|
}
|
|
70
99
|
|
|
71
100
|
// src/network.ts
|
|
72
|
-
var fetch = globalThis.fetch;
|
|
73
|
-
function defineFetch(customFetch) {
|
|
74
|
-
fetch = customFetch;
|
|
75
|
-
}
|
|
76
101
|
async function requestText(url, userFriendlySource) {
|
|
77
102
|
return request(url, userFriendlySource).then((res) => res.text());
|
|
78
103
|
}
|
|
79
104
|
async function request(url, userFriendlySource) {
|
|
105
|
+
const { fetch } = config;
|
|
80
106
|
let response;
|
|
81
107
|
try {
|
|
82
108
|
response = await fetch(url);
|
|
@@ -93,22 +119,351 @@ async function request(url, userFriendlySource) {
|
|
|
93
119
|
return response;
|
|
94
120
|
}
|
|
95
121
|
|
|
96
|
-
// src/
|
|
97
|
-
var
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
// src/signals.ts
|
|
123
|
+
var activeEffect = null;
|
|
124
|
+
var jobQueue = [];
|
|
125
|
+
var isFlushPending = false;
|
|
126
|
+
function queueJob(job) {
|
|
127
|
+
if (!jobQueue.includes(job)) {
|
|
128
|
+
jobQueue.push(job);
|
|
129
|
+
}
|
|
130
|
+
if (!isFlushPending) {
|
|
131
|
+
isFlushPending = true;
|
|
132
|
+
Promise.resolve().then(flushJobs);
|
|
133
|
+
}
|
|
101
134
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
function flushJobs() {
|
|
136
|
+
isFlushPending = false;
|
|
137
|
+
const jobs = [...jobQueue];
|
|
138
|
+
jobQueue.length = 0;
|
|
139
|
+
jobs.forEach((job) => job());
|
|
140
|
+
}
|
|
141
|
+
function cleanup(effect2) {
|
|
142
|
+
effect2.deps.forEach((dep) => {
|
|
143
|
+
dep.delete(effect2);
|
|
144
|
+
});
|
|
145
|
+
effect2.deps.clear();
|
|
146
|
+
}
|
|
147
|
+
function createReactiveEffect(fn, deps = /* @__PURE__ */ new Set(), options) {
|
|
148
|
+
const effect2 = fn;
|
|
149
|
+
effect2.deps = deps;
|
|
150
|
+
effect2.options = options;
|
|
151
|
+
return effect2;
|
|
152
|
+
}
|
|
153
|
+
var EffectScope = class {
|
|
154
|
+
effects = [];
|
|
155
|
+
active = true;
|
|
156
|
+
run(fn) {
|
|
157
|
+
if (!this.active) return;
|
|
158
|
+
const prevScope = activeScope;
|
|
159
|
+
activeScope = this;
|
|
160
|
+
try {
|
|
161
|
+
return fn();
|
|
162
|
+
} finally {
|
|
163
|
+
activeScope = prevScope;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
add(stopFn) {
|
|
167
|
+
if (this.active) {
|
|
168
|
+
this.effects.push(stopFn);
|
|
169
|
+
} else {
|
|
170
|
+
stopFn();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
stop() {
|
|
174
|
+
if (this.active) {
|
|
175
|
+
this.effects.forEach((stop) => stop());
|
|
176
|
+
this.effects = [];
|
|
177
|
+
this.active = false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
var activeScope = null;
|
|
182
|
+
function effectScope(fn) {
|
|
183
|
+
const scope = new EffectScope();
|
|
184
|
+
if (fn) scope.run(fn);
|
|
185
|
+
return () => scope.stop();
|
|
186
|
+
}
|
|
187
|
+
function effect(fn) {
|
|
188
|
+
const effect2 = createReactiveEffect(
|
|
189
|
+
() => {
|
|
190
|
+
cleanup(effect2);
|
|
191
|
+
const prevEffect = activeEffect;
|
|
192
|
+
activeEffect = effect2;
|
|
193
|
+
try {
|
|
194
|
+
fn();
|
|
195
|
+
} finally {
|
|
196
|
+
activeEffect = prevEffect;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
/* @__PURE__ */ new Set(),
|
|
200
|
+
{ scheduler: queueJob }
|
|
201
|
+
);
|
|
202
|
+
effect2();
|
|
203
|
+
const stop = () => {
|
|
204
|
+
cleanup(effect2);
|
|
106
205
|
};
|
|
206
|
+
if (activeScope) {
|
|
207
|
+
activeScope.add(stop);
|
|
208
|
+
}
|
|
209
|
+
return stop;
|
|
210
|
+
}
|
|
211
|
+
function signal(initialValue) {
|
|
212
|
+
let value = initialValue;
|
|
213
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
214
|
+
const read = () => {
|
|
215
|
+
if (activeEffect) {
|
|
216
|
+
subscribers.add(activeEffect);
|
|
217
|
+
activeEffect.deps.add(subscribers);
|
|
218
|
+
}
|
|
219
|
+
return value;
|
|
220
|
+
};
|
|
221
|
+
read.toString = () => {
|
|
222
|
+
throw new NativeSFCError(
|
|
223
|
+
`signal<<${value}>>: This is a signal reader, you MUST call it to get the value.`
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
const write = (newValue) => {
|
|
227
|
+
if (value !== newValue) {
|
|
228
|
+
value = newValue;
|
|
229
|
+
const effectsToRun = new Set(subscribers);
|
|
230
|
+
effectsToRun.forEach((effect2) => {
|
|
231
|
+
if (effect2.options?.scheduler) {
|
|
232
|
+
effect2.options.scheduler(effect2);
|
|
233
|
+
} else {
|
|
234
|
+
effect2();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
write.toString = () => {
|
|
240
|
+
throw new NativeSFCError(
|
|
241
|
+
`signal<<${value}>>: This is a signal writer, you MUST call it to set the value.`
|
|
242
|
+
);
|
|
243
|
+
};
|
|
244
|
+
return [read, write];
|
|
245
|
+
}
|
|
246
|
+
function computed(fn) {
|
|
247
|
+
let value;
|
|
248
|
+
let dirty = true;
|
|
249
|
+
let isComputing = false;
|
|
250
|
+
const runner = () => {
|
|
251
|
+
if (!dirty) {
|
|
252
|
+
dirty = true;
|
|
253
|
+
trigger(subscribers);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const internalEffect = createReactiveEffect(runner);
|
|
257
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
258
|
+
const trigger = (subs) => {
|
|
259
|
+
const effectsToRun = new Set(subs);
|
|
260
|
+
effectsToRun.forEach((effect2) => {
|
|
261
|
+
if (effect2.options?.scheduler) {
|
|
262
|
+
effect2.options.scheduler(effect2);
|
|
263
|
+
} else {
|
|
264
|
+
effect2();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
const read = () => {
|
|
269
|
+
if (isComputing) {
|
|
270
|
+
throw new NativeSFCError(`Circular dependency detected in computed<<${value}>>`);
|
|
271
|
+
}
|
|
272
|
+
if (activeEffect) {
|
|
273
|
+
subscribers.add(activeEffect);
|
|
274
|
+
activeEffect.deps.add(subscribers);
|
|
275
|
+
}
|
|
276
|
+
if (dirty) {
|
|
277
|
+
isComputing = true;
|
|
278
|
+
const prevEffect = activeEffect;
|
|
279
|
+
activeEffect = internalEffect;
|
|
280
|
+
cleanup(internalEffect);
|
|
281
|
+
try {
|
|
282
|
+
value = fn();
|
|
283
|
+
dirty = false;
|
|
284
|
+
} finally {
|
|
285
|
+
activeEffect = prevEffect;
|
|
286
|
+
isComputing = false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return value;
|
|
290
|
+
};
|
|
291
|
+
read.toString = () => {
|
|
292
|
+
throw new NativeSFCError(
|
|
293
|
+
`computed<<${value}>>: This is a computed reader, you MUST call it to get the value.`
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
return read;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/template.ts
|
|
300
|
+
function toCamelCase(str) {
|
|
301
|
+
return str.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
302
|
+
}
|
|
303
|
+
function parseTextContent(text) {
|
|
304
|
+
const regex = /\{\{(.+?)\}\}/g;
|
|
305
|
+
const parts = [];
|
|
306
|
+
let lastIndex = 0;
|
|
307
|
+
let match;
|
|
308
|
+
while ((match = regex.exec(text)) !== null) {
|
|
309
|
+
if (match.index > lastIndex) {
|
|
310
|
+
parts.push({
|
|
311
|
+
type: "static",
|
|
312
|
+
content: text.slice(lastIndex, match.index)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
parts.push({
|
|
316
|
+
type: "dynamic",
|
|
317
|
+
content: match[1].trim()
|
|
318
|
+
});
|
|
319
|
+
lastIndex = regex.lastIndex;
|
|
320
|
+
}
|
|
321
|
+
if (lastIndex < text.length) {
|
|
322
|
+
parts.push({
|
|
323
|
+
type: "static",
|
|
324
|
+
content: text.slice(lastIndex)
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
return parts;
|
|
328
|
+
}
|
|
329
|
+
function reactiveNodes(nodes, context) {
|
|
330
|
+
const evalExpr = (expr, additionalContext = {}) => {
|
|
331
|
+
const ctx = typeof context === "object" ? Object.assign({}, context, additionalContext) : additionalContext;
|
|
332
|
+
const keys = Object.keys(ctx);
|
|
333
|
+
const values = Object.values(ctx);
|
|
334
|
+
const func = new Function(...keys, `return ${expr.trimStart()}`);
|
|
335
|
+
return func(...values);
|
|
336
|
+
};
|
|
337
|
+
const recursive = (nodes2) => {
|
|
338
|
+
for (const node of nodes2) {
|
|
339
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
340
|
+
const element = node;
|
|
341
|
+
const ifAttr = element.getAttribute("#if");
|
|
342
|
+
if (ifAttr) {
|
|
343
|
+
if (element.hasAttribute("#for")) {
|
|
344
|
+
console.warn("Cannot use #if and #for on the same element");
|
|
345
|
+
}
|
|
346
|
+
const template = element.cloneNode(true);
|
|
347
|
+
const parent = element.parentNode;
|
|
348
|
+
const placeholder = document.createComment("if");
|
|
349
|
+
parent?.replaceChild(placeholder, element);
|
|
350
|
+
template.removeAttribute("#if");
|
|
351
|
+
let renderedNode = null;
|
|
352
|
+
let cleanup2 = null;
|
|
353
|
+
effect(() => {
|
|
354
|
+
const condition = evalExpr(ifAttr);
|
|
355
|
+
if (condition) {
|
|
356
|
+
if (!renderedNode) {
|
|
357
|
+
const clone = template.cloneNode(true);
|
|
358
|
+
cleanup2 = reactiveNodes([clone], context);
|
|
359
|
+
placeholder.parentNode?.insertBefore(clone, placeholder.nextSibling);
|
|
360
|
+
renderedNode = clone;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (renderedNode) {
|
|
364
|
+
cleanup2?.();
|
|
365
|
+
renderedNode.remove();
|
|
366
|
+
renderedNode = null;
|
|
367
|
+
cleanup2 = null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
const forAttr = element.getAttribute("#for");
|
|
374
|
+
if (forAttr) {
|
|
375
|
+
const template = element.cloneNode(true);
|
|
376
|
+
const parent = element.parentNode;
|
|
377
|
+
const placeholder = document.createComment("for");
|
|
378
|
+
parent?.replaceChild(placeholder, element);
|
|
379
|
+
template.removeAttribute("#for");
|
|
380
|
+
let renderedItems = [];
|
|
381
|
+
effect(() => {
|
|
382
|
+
const contexts = evalExpr(forAttr);
|
|
383
|
+
if (!Array.isArray(contexts)) {
|
|
384
|
+
console.warn("#for expression must return an array");
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
renderedItems.forEach(({ node: node2, cleanup: cleanup2 }) => {
|
|
388
|
+
cleanup2();
|
|
389
|
+
node2.remove();
|
|
390
|
+
});
|
|
391
|
+
renderedItems = [];
|
|
392
|
+
contexts.forEach((itemContext) => {
|
|
393
|
+
const clone = template.cloneNode(true);
|
|
394
|
+
const cleanup2 = reactiveNodes([clone], { ...context, ...itemContext });
|
|
395
|
+
placeholder.parentNode?.insertBefore(clone, placeholder.nextSibling);
|
|
396
|
+
renderedItems.push({ node: clone, cleanup: cleanup2 });
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
for (const attr of Array.from(element.attributes)) {
|
|
402
|
+
if (attr.name.startsWith(".")) {
|
|
403
|
+
const propName = toCamelCase(attr.name.slice(1));
|
|
404
|
+
const expr = attr.value;
|
|
405
|
+
effect(() => {
|
|
406
|
+
const value = evalExpr(expr);
|
|
407
|
+
Reflect.set(element, propName, value);
|
|
408
|
+
});
|
|
409
|
+
element.removeAttribute(attr.name);
|
|
410
|
+
} else if (attr.name.startsWith(":")) {
|
|
411
|
+
const attrName = attr.name.slice(1);
|
|
412
|
+
const expr = attr.value;
|
|
413
|
+
effect(() => {
|
|
414
|
+
const value = evalExpr(expr);
|
|
415
|
+
element.setAttribute(attrName, value);
|
|
416
|
+
});
|
|
417
|
+
element.removeAttribute(attr.name);
|
|
418
|
+
} else if (attr.name.startsWith("@")) {
|
|
419
|
+
const eventName = attr.name.slice(1);
|
|
420
|
+
const expr = attr.value;
|
|
421
|
+
const listener = computed(() => (event) => {
|
|
422
|
+
evalExpr(expr, { event });
|
|
423
|
+
})();
|
|
424
|
+
element.addEventListener(eventName, listener);
|
|
425
|
+
element.removeAttribute(attr.name);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
} else if (node.nodeType === Node.TEXT_NODE) {
|
|
429
|
+
const textNode = node;
|
|
430
|
+
const text = textNode.textContent || "";
|
|
431
|
+
const parts = parseTextContent(text);
|
|
432
|
+
if (parts.some((part) => part.type === "dynamic")) {
|
|
433
|
+
const parentNode = textNode.parentNode;
|
|
434
|
+
if (parentNode) {
|
|
435
|
+
const fragment = document.createDocumentFragment();
|
|
436
|
+
const textNodes = [];
|
|
437
|
+
for (const part of parts) {
|
|
438
|
+
const newTextNode = document.createTextNode(
|
|
439
|
+
part.type === "static" ? part.content : ""
|
|
440
|
+
);
|
|
441
|
+
fragment.appendChild(newTextNode);
|
|
442
|
+
textNodes.push(newTextNode);
|
|
443
|
+
if (part.type === "dynamic") {
|
|
444
|
+
effect(() => {
|
|
445
|
+
const value = evalExpr(part.content);
|
|
446
|
+
newTextNode.textContent = String(value);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
parentNode.replaceChild(fragment, textNode);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (node.childNodes.length > 0) {
|
|
455
|
+
recursive(node.childNodes);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
return effectScope(() => {
|
|
460
|
+
recursive(nodes);
|
|
461
|
+
});
|
|
107
462
|
}
|
|
108
463
|
|
|
109
464
|
// src/components.ts
|
|
110
465
|
var loadedComponentsRecord = /* @__PURE__ */ new Map();
|
|
111
|
-
async function loadComponent(name, url
|
|
466
|
+
async function loadComponent(name, url) {
|
|
112
467
|
const importerUrl = getImporterUrl() || location.href;
|
|
113
468
|
url = new URL(url, importerUrl).href;
|
|
114
469
|
emit("component-loading", { name, url });
|
|
@@ -142,11 +497,11 @@ async function loadComponent(name, url, afterConstructor) {
|
|
|
142
497
|
);
|
|
143
498
|
}
|
|
144
499
|
const define = (component2) => {
|
|
145
|
-
const
|
|
146
|
-
customElements.define(name,
|
|
500
|
+
const CEC = extendsElement(component2, doc.body.innerHTML, adoptedStyleSheets);
|
|
501
|
+
customElements.define(name, CEC);
|
|
147
502
|
emit("component-defined", { name, url });
|
|
148
|
-
loadedComponentsRecord.set(name, { cec, url });
|
|
149
|
-
return
|
|
503
|
+
loadedComponentsRecord.set(name, { cec: CEC, url });
|
|
504
|
+
return CEC;
|
|
150
505
|
};
|
|
151
506
|
if (!component || !defaultExportIsComponent) {
|
|
152
507
|
return define(HTMLElement);
|
|
@@ -154,7 +509,7 @@ async function loadComponent(name, url, afterConstructor) {
|
|
|
154
509
|
return define(component);
|
|
155
510
|
}
|
|
156
511
|
}
|
|
157
|
-
function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets
|
|
512
|
+
function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets) {
|
|
158
513
|
return class extends BaseClass {
|
|
159
514
|
constructor(...args) {
|
|
160
515
|
super(innerHTML, adoptedStyleSheets);
|
|
@@ -165,22 +520,42 @@ function extendsElement(BaseClass = HTMLElement, innerHTML, adoptedStyleSheets,
|
|
|
165
520
|
shadowRoot.adoptedStyleSheets = adoptedStyleSheets;
|
|
166
521
|
}
|
|
167
522
|
}
|
|
168
|
-
if (
|
|
169
|
-
|
|
523
|
+
if ("setup" in this && typeof this.setup === "function") {
|
|
524
|
+
const context = this.setup();
|
|
525
|
+
reactiveNodes(this.shadowRoot.childNodes, context);
|
|
526
|
+
} else {
|
|
527
|
+
reactiveNodes(this.shadowRoot.childNodes, {});
|
|
170
528
|
}
|
|
171
529
|
}
|
|
172
530
|
};
|
|
173
531
|
}
|
|
174
|
-
function defineComponent(
|
|
175
|
-
const whoDefineMe =
|
|
532
|
+
function defineComponent(setup) {
|
|
533
|
+
const whoDefineMe = stackTraceParser.parse(new Error().stack).at(-1).file;
|
|
176
534
|
if (blobMap.has(whoDefineMe)) {
|
|
177
535
|
return class extends HTMLElement {
|
|
536
|
+
_onConnectedEvents = [];
|
|
537
|
+
_onDisconnectedEvents = [];
|
|
538
|
+
setup() {
|
|
539
|
+
return setup({
|
|
540
|
+
onConnected: (event) => this._onConnectedEvents.push(event),
|
|
541
|
+
onDisconnected: (event) => this._onDisconnectedEvents.push(event)
|
|
542
|
+
});
|
|
543
|
+
}
|
|
178
544
|
connectedCallback() {
|
|
179
|
-
|
|
545
|
+
const root = this.shadowRoot || this.attachShadow({ mode: "open" });
|
|
546
|
+
this._onConnectedEvents.forEach((cb) => cb.call(void 0, root));
|
|
547
|
+
}
|
|
548
|
+
disconnectedCallback() {
|
|
549
|
+
const root = this.shadowRoot;
|
|
550
|
+
this._onDisconnectedEvents.forEach((cb) => cb.call(void 0, root));
|
|
180
551
|
}
|
|
181
552
|
};
|
|
182
553
|
}
|
|
183
|
-
return
|
|
554
|
+
return setup({
|
|
555
|
+
onConnected: (cb) => cb(document),
|
|
556
|
+
onDisconnected: () => {
|
|
557
|
+
}
|
|
558
|
+
});
|
|
184
559
|
}
|
|
185
560
|
function filterGlobalStyle(doc) {
|
|
186
561
|
for (const styleElement of doc.querySelectorAll("style")) {
|
|
@@ -263,10 +638,13 @@ async function evaluateModules(doc, url) {
|
|
|
263
638
|
}
|
|
264
639
|
export {
|
|
265
640
|
NativeSFCError,
|
|
641
|
+
computed,
|
|
642
|
+
config,
|
|
266
643
|
defineComponent,
|
|
267
|
-
|
|
644
|
+
effect,
|
|
645
|
+
effectScope,
|
|
268
646
|
loadComponent,
|
|
269
|
-
|
|
647
|
+
signal
|
|
270
648
|
};
|
|
271
649
|
//! we provide an extra argument to user's component constructor
|
|
272
650
|
//! if the user's constructor does not create a shadow root, we will create one here
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
*
|
|
33
33
|
* @param name - The custom element tag name (must contain a hyphen, e.g., "my-component")
|
|
34
34
|
* @param url - URL to the component HTML file (relative to the importer or absolute)
|
|
35
|
-
* @param afterConstructor - Optional callback invoked after component constructor completes
|
|
36
35
|
* @returns The CustomElementConstructor for the registered component
|
|
37
36
|
* @throws {NativeSFCError} If the component name is already registered by external code
|
|
38
37
|
*
|
|
@@ -41,18 +40,50 @@
|
|
|
41
40
|
* await loadComponent('my-button', './components/my-button.html');
|
|
42
41
|
* document.body.innerHTML = '<my-button>Click me</my-button>';
|
|
43
42
|
*/
|
|
44
|
-
export declare function loadComponent(name: string, url: string
|
|
43
|
+
export declare function loadComponent(name: string, url: string): Promise<CustomElementConstructor>;
|
|
45
44
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
45
|
+
* A dual-purpose component definition helper that adapts behavior based on execution context.
|
|
46
|
+
*
|
|
47
|
+
* When used inside a loadComponent-imported module, returns a web component class that
|
|
48
|
+
* manages lifecycle callbacks (onConnected, onDisconnected) and invokes them with the
|
|
49
|
+
* component's ShadowRoot. In normal document context, immediately executes the function
|
|
50
|
+
* with `document` as root.
|
|
51
|
+
*
|
|
52
|
+
* @param setup - A function that receives lifecycle callback registration methods
|
|
53
|
+
* and returns component state/context.
|
|
54
|
+
* @returns When used in component context: a CustomElementConstructor class.
|
|
55
|
+
* When used in document context: the return value of `setup`.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* // In a component HTML file (loaded via loadComponent):
|
|
59
|
+
* // <script type="module">
|
|
60
|
+
* import { defineComponent, signal } from 'native-sfc';
|
|
61
|
+
*
|
|
62
|
+
* export default defineComponent(({ onConnected, onDisconnected }) => {
|
|
63
|
+
* const nonReactiveState = { count: 0 };
|
|
64
|
+
*
|
|
65
|
+
* onConnected((root) => {
|
|
66
|
+
* const button = root.querySelector('button');
|
|
67
|
+
* button?.addEventListener('click', () => {
|
|
68
|
+
* nonReactiveState.count++;
|
|
69
|
+
* console.log('count:', nonReactiveState.count);
|
|
70
|
+
* });
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* onDisconnected((root) => {
|
|
74
|
+
* console.log('component removed');
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* const [increment, setIncrement] = signal(0);
|
|
78
|
+
*
|
|
79
|
+
* return { increment, setIncrement }; // reactive state can be used in template
|
|
80
|
+
* });
|
|
81
|
+
* // </script>
|
|
49
82
|
*/
|
|
50
|
-
export declare function defineComponent(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
declare let fetch$1: typeof globalThis.fetch;
|
|
55
|
-
export declare function defineFetch(customFetch: typeof fetch$1): void;
|
|
83
|
+
export declare function defineComponent(setup: ({ onConnected, onDisconnected, }: {
|
|
84
|
+
onConnected: (cb: (root: ShadowRoot | Document) => void) => void;
|
|
85
|
+
onDisconnected: (cb: (root: ShadowRoot) => void) => void;
|
|
86
|
+
}) => any): unknown;
|
|
56
87
|
export interface NativeSFCEvents {
|
|
57
88
|
"component-loading": {
|
|
58
89
|
name: string;
|
|
@@ -66,6 +97,24 @@ export interface NativeSFCEvents {
|
|
|
66
97
|
export type NativeSFCEventsMap = {
|
|
67
98
|
[K in keyof NativeSFCEvents]: CustomEvent<NativeSFCEvents[K]>;
|
|
68
99
|
};
|
|
69
|
-
|
|
100
|
+
declare function on<K extends keyof NativeSFCEvents>(eventName: K, listener: (ev: NativeSFCEventsMap[K]) => any): () => void;
|
|
101
|
+
export declare const config: {
|
|
102
|
+
fetch: typeof fetch;
|
|
103
|
+
rewriteModule: (code: string, sourceUrl: string) => Awaitable<string>;
|
|
104
|
+
on: typeof on;
|
|
105
|
+
};
|
|
106
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
107
|
+
export declare class NativeSFCError extends Error {
|
|
108
|
+
constructor(message: string, options?: ErrorOptions);
|
|
109
|
+
}
|
|
110
|
+
export type Getter<T> = () => T;
|
|
111
|
+
export type Setter<T> = (newValue: T) => void;
|
|
112
|
+
export declare function effectScope(fn?: VoidFunction): VoidFunction;
|
|
113
|
+
export declare function effect(fn: VoidFunction): VoidFunction;
|
|
114
|
+
export declare function signal<T>(initialValue: T): readonly [
|
|
115
|
+
Getter<T>,
|
|
116
|
+
Setter<T>
|
|
117
|
+
];
|
|
118
|
+
export declare function computed<T>(fn: () => T): Getter<T>;
|
|
70
119
|
|
|
71
120
|
export {};
|