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