solid-tag-runtime 0.0.7 → 0.0.8
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/ARCHITECTURE.md +113 -0
- package/README.md +118 -15
- package/html.d.ts +49 -0
- package/index.d.ts +42 -1
- package/package.json +1 -1
- package/src/events.js +63 -0
- package/src/html.js +299 -24
- package/src/runtime.js +198 -94
package/src/runtime.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
SolidTagRuntimeError,
|
|
6
6
|
} from "./errors.js";
|
|
7
7
|
import { createDefaultModuleUrlBackend } from "./url-backend.js";
|
|
8
|
+
import { createEventDispatcher } from "./events.js";
|
|
8
9
|
|
|
9
10
|
const HOST_REGISTRY_KEY = "__solidTagRuntimeHostModules__";
|
|
10
11
|
let runtimeSequence = 0;
|
|
@@ -27,7 +28,11 @@ export function createRuntime(options = {}) {
|
|
|
27
28
|
const records = new Map();
|
|
28
29
|
let anonymousSequence = 0;
|
|
29
30
|
let disposed = false;
|
|
30
|
-
const
|
|
31
|
+
const events = createEventDispatcher({
|
|
32
|
+
onListenerError(error) {
|
|
33
|
+
globalThis.console?.error?.("solid-tag-runtime lifecycle subscriber error", error);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
31
36
|
let eventBufferDepth = 0;
|
|
32
37
|
const eventBuffer = [];
|
|
33
38
|
|
|
@@ -102,7 +107,13 @@ export function createRuntime(options = {}) {
|
|
|
102
107
|
|
|
103
108
|
eventBufferDepth += 1;
|
|
104
109
|
try {
|
|
105
|
-
|
|
110
|
+
const ids = prepared.map(item => defineSource(item.id, item.source, { format: item.format }, "defineMany"));
|
|
111
|
+
emit({
|
|
112
|
+
type: "modules-defined",
|
|
113
|
+
ids: [...ids],
|
|
114
|
+
modules: ids.map(id => toModuleInfo(records.get(id))),
|
|
115
|
+
});
|
|
116
|
+
return ids;
|
|
106
117
|
} finally {
|
|
107
118
|
eventBufferDepth -= 1;
|
|
108
119
|
if (eventBufferDepth === 0) flushEventBuffer();
|
|
@@ -146,6 +157,17 @@ export function createRuntime(options = {}) {
|
|
|
146
157
|
replaced: Boolean(existing),
|
|
147
158
|
module: toModuleInfo(record),
|
|
148
159
|
});
|
|
160
|
+
if (operation === "update") {
|
|
161
|
+
emit({
|
|
162
|
+
type: "module-updated",
|
|
163
|
+
id: normalized,
|
|
164
|
+
kind: record.kind,
|
|
165
|
+
previousVersion: existing?.version ?? 0,
|
|
166
|
+
version: record.version,
|
|
167
|
+
format: record.format,
|
|
168
|
+
module: toModuleInfo(record),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
149
171
|
return normalized;
|
|
150
172
|
}
|
|
151
173
|
|
|
@@ -251,9 +273,11 @@ export function createRuntime(options = {}) {
|
|
|
251
273
|
}
|
|
252
274
|
|
|
253
275
|
if (!record.importPromise) {
|
|
254
|
-
record.
|
|
255
|
-
|
|
256
|
-
|
|
276
|
+
record.importPromise = ensureSourceUrl(record, []).then(url => {
|
|
277
|
+
record.state = "evaluating";
|
|
278
|
+
emit({ type: "module-evaluating", id: record.id, kind: record.kind, version: record.version });
|
|
279
|
+
return import(url);
|
|
280
|
+
}).then(namespace => {
|
|
257
281
|
record.namespace = namespace;
|
|
258
282
|
record.state = "ready";
|
|
259
283
|
emit({ type: "module-evaluated", id: record.id, kind: record.kind, version: record.version, module: toModuleInfo(record) });
|
|
@@ -421,13 +445,9 @@ export function createRuntime(options = {}) {
|
|
|
421
445
|
return ids;
|
|
422
446
|
}
|
|
423
447
|
|
|
424
|
-
function subscribe(
|
|
448
|
+
function subscribe(typesOrListener, maybeListener) {
|
|
425
449
|
assertActive();
|
|
426
|
-
|
|
427
|
-
throw new TypeError("subscribe(listener) expects a function.");
|
|
428
|
-
}
|
|
429
|
-
subscribers.add(listener);
|
|
430
|
-
return () => subscribers.delete(listener);
|
|
450
|
+
return events.subscribe(typesOrListener, maybeListener);
|
|
431
451
|
}
|
|
432
452
|
|
|
433
453
|
function listModules() {
|
|
@@ -458,7 +478,7 @@ export function createRuntime(options = {}) {
|
|
|
458
478
|
records.clear();
|
|
459
479
|
hostNamespaces.clear();
|
|
460
480
|
globalRegistry.delete(runtimeId);
|
|
461
|
-
|
|
481
|
+
events.clear();
|
|
462
482
|
}
|
|
463
483
|
|
|
464
484
|
function resolvePublic(specifier, importer) {
|
|
@@ -470,11 +490,19 @@ export function createRuntime(options = {}) {
|
|
|
470
490
|
|
|
471
491
|
if (stack.includes(record.id)) {
|
|
472
492
|
const cycleStart = stack.indexOf(record.id);
|
|
473
|
-
|
|
493
|
+
const error = new CircularDependencyError([...stack.slice(cycleStart), record.id]);
|
|
494
|
+
emitError(record, "link", error);
|
|
495
|
+
throw error;
|
|
474
496
|
}
|
|
475
497
|
|
|
476
498
|
const nextStack = [...stack, record.id];
|
|
477
499
|
record.state = "linking";
|
|
500
|
+
emit({
|
|
501
|
+
type: "module-linking",
|
|
502
|
+
id: record.id,
|
|
503
|
+
kind: record.kind,
|
|
504
|
+
version: record.version,
|
|
505
|
+
});
|
|
478
506
|
|
|
479
507
|
let analysis;
|
|
480
508
|
try {
|
|
@@ -487,79 +515,126 @@ export function createRuntime(options = {}) {
|
|
|
487
515
|
}
|
|
488
516
|
|
|
489
517
|
const resolvedImports = [];
|
|
518
|
+
const linkedDependencies = [];
|
|
490
519
|
const nextDependencies = new Set();
|
|
491
520
|
|
|
492
521
|
for (const reference of analysis.imports ?? []) {
|
|
493
|
-
|
|
522
|
+
let resolved;
|
|
523
|
+
try {
|
|
524
|
+
resolved = resolveSpecifier(reference.specifier, record.id);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
record.state = "error";
|
|
527
|
+
emitError(record, "resolve", error);
|
|
528
|
+
throw error;
|
|
529
|
+
}
|
|
530
|
+
|
|
494
531
|
const dependencyRecord = records.get(resolved);
|
|
495
532
|
let replacement = resolved;
|
|
496
533
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
534
|
+
try {
|
|
535
|
+
if (dependencyRecord) {
|
|
536
|
+
nextDependencies.add(dependencyRecord.id);
|
|
537
|
+
|
|
538
|
+
if (dependencyRecord.kind === "source") {
|
|
539
|
+
replacement = await ensureSourceUrl(dependencyRecord, nextStack);
|
|
540
|
+
} else if (dependencyRecord.kind === "host") {
|
|
541
|
+
replacement = await ensureHostUrl(dependencyRecord);
|
|
542
|
+
} else {
|
|
543
|
+
replacement = dependencyRecord.externalUrl;
|
|
544
|
+
}
|
|
545
|
+
} else if (isRelativeSpecifier(reference.specifier) || reference.specifier.startsWith("/")) {
|
|
546
|
+
throw new ModuleResolutionError(reference.specifier, record.id);
|
|
547
|
+
} else if (!allowNativeImports && !isAbsoluteUrl(resolved)) {
|
|
548
|
+
throw new ModuleResolutionError(reference.specifier, record.id);
|
|
506
549
|
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
throw
|
|
550
|
+
} catch (error) {
|
|
551
|
+
record.state = "error";
|
|
552
|
+
emitError(record, error instanceof ModuleResolutionError ? "resolve" : "link", error);
|
|
553
|
+
throw error;
|
|
511
554
|
}
|
|
512
555
|
|
|
513
556
|
resolvedImports.push({ ...reference, replacement });
|
|
557
|
+
linkedDependencies.push({
|
|
558
|
+
specifier: reference.specifier,
|
|
559
|
+
resolvedId: dependencyRecord?.id ?? resolved,
|
|
560
|
+
});
|
|
514
561
|
}
|
|
515
562
|
|
|
516
|
-
let linkedSource
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
? await
|
|
532
|
-
: htmlRecord.
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
563
|
+
let linkedSource;
|
|
564
|
+
try {
|
|
565
|
+
linkedSource = applyImportReplacements(record.source, resolvedImports);
|
|
566
|
+
|
|
567
|
+
// JSX produced by solid-tag needs @solidjs/html. Resolve it through the
|
|
568
|
+
// same module system so host applications can share their existing runtime.
|
|
569
|
+
let htmlImportSource;
|
|
570
|
+
const needsHtmlRuntime = record.format !== "js" && analysis.needsHtmlRuntime !== false;
|
|
571
|
+
if (needsHtmlRuntime) {
|
|
572
|
+
const htmlResolved = resolveSpecifier("@solidjs/html", record.id);
|
|
573
|
+
const htmlRecord = records.get(htmlResolved);
|
|
574
|
+
|
|
575
|
+
if (htmlRecord) {
|
|
576
|
+
nextDependencies.add(htmlRecord.id);
|
|
577
|
+
htmlImportSource = htmlRecord.kind === "source"
|
|
578
|
+
? await ensureSourceUrl(htmlRecord, nextStack)
|
|
579
|
+
: htmlRecord.kind === "host"
|
|
580
|
+
? await ensureHostUrl(htmlRecord)
|
|
581
|
+
: htmlRecord.externalUrl;
|
|
582
|
+
} else if (allowNativeImports) {
|
|
583
|
+
htmlImportSource = htmlResolved;
|
|
584
|
+
} else {
|
|
585
|
+
throw new ModuleResolutionError("@solidjs/html", record.id);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
linkedDependencies.push({
|
|
589
|
+
specifier: "@solidjs/html",
|
|
590
|
+
resolvedId: htmlRecord?.id ?? htmlResolved,
|
|
591
|
+
});
|
|
537
592
|
}
|
|
538
|
-
}
|
|
539
593
|
|
|
540
|
-
|
|
594
|
+
syncDependencyEdges(record, nextDependencies);
|
|
595
|
+
emit({
|
|
596
|
+
type: "module-linked",
|
|
597
|
+
id: record.id,
|
|
598
|
+
kind: record.kind,
|
|
599
|
+
version: record.version,
|
|
600
|
+
dependencies: linkedDependencies,
|
|
601
|
+
module: toModuleInfo(record),
|
|
602
|
+
});
|
|
541
603
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
604
|
+
try {
|
|
605
|
+
if (record.format === "js") {
|
|
606
|
+
record.compiledCode = linkedSource;
|
|
607
|
+
record.diagnostics = [];
|
|
608
|
+
} else {
|
|
609
|
+
const transformed = compiler.transform(linkedSource, {
|
|
610
|
+
id: record.id,
|
|
611
|
+
format: record.format,
|
|
612
|
+
htmlImportSource,
|
|
613
|
+
});
|
|
614
|
+
record.compiledCode = transformed.code;
|
|
615
|
+
record.diagnostics = [...(transformed.diagnostics ?? [])];
|
|
616
|
+
}
|
|
617
|
+
} catch (error) {
|
|
618
|
+
record.state = "error";
|
|
619
|
+
const wrapped = new ModuleCompileError(record.id, error);
|
|
620
|
+
emitError(record, "compile", wrapped);
|
|
621
|
+
throw wrapped;
|
|
554
622
|
}
|
|
555
623
|
} catch (error) {
|
|
556
|
-
record.state
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
624
|
+
if (record.state !== "error") {
|
|
625
|
+
record.state = "error";
|
|
626
|
+
emitError(record, error instanceof ModuleResolutionError ? "resolve" : "link", error);
|
|
627
|
+
}
|
|
628
|
+
throw error;
|
|
560
629
|
}
|
|
561
630
|
|
|
562
|
-
|
|
631
|
+
try {
|
|
632
|
+
record.url = moduleUrlBackend.create(appendSourceUrl(record.compiledCode, record.id));
|
|
633
|
+
} catch (error) {
|
|
634
|
+
record.state = "error";
|
|
635
|
+
emitError(record, "url-create", error);
|
|
636
|
+
throw error;
|
|
637
|
+
}
|
|
563
638
|
record.state = "compiled";
|
|
564
639
|
emit({
|
|
565
640
|
type: "module-compiled",
|
|
@@ -597,30 +672,69 @@ export function createRuntime(options = {}) {
|
|
|
597
672
|
);
|
|
598
673
|
}
|
|
599
674
|
|
|
600
|
-
|
|
675
|
+
try {
|
|
676
|
+
record.url = moduleUrlBackend.create(appendSourceUrl(lines.join("\n"), `host:${record.id}`));
|
|
677
|
+
} catch (error) {
|
|
678
|
+
emitError(record, "host-bridge", error);
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
601
681
|
return record.url;
|
|
602
682
|
}
|
|
603
683
|
|
|
604
684
|
function resolveSpecifier(specifier, importer) {
|
|
605
685
|
const raw = String(specifier);
|
|
686
|
+
emit({
|
|
687
|
+
type: "module-resolving",
|
|
688
|
+
specifier: raw,
|
|
689
|
+
importer: importer ?? null,
|
|
690
|
+
});
|
|
606
691
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (typeof custom === "string" && custom) return normalizeResolved(custom, importer);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (records.has(raw)) return raw;
|
|
613
|
-
if (isAbsoluteUrl(raw)) return raw;
|
|
692
|
+
let resolved;
|
|
693
|
+
let resolutionKind;
|
|
614
694
|
|
|
615
|
-
|
|
695
|
+
try {
|
|
696
|
+
if (typeof customResolve === "function") {
|
|
697
|
+
const custom = customResolve(raw, importer, api);
|
|
698
|
+
if (typeof custom === "string" && custom) {
|
|
699
|
+
resolved = normalizeResolved(custom, importer);
|
|
700
|
+
resolutionKind = "custom";
|
|
701
|
+
}
|
|
702
|
+
}
|
|
616
703
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
704
|
+
if (resolved == null && records.has(raw)) {
|
|
705
|
+
resolved = raw;
|
|
706
|
+
resolutionKind = "registered";
|
|
707
|
+
}
|
|
708
|
+
if (resolved == null && isAbsoluteUrl(raw)) {
|
|
709
|
+
resolved = raw;
|
|
710
|
+
resolutionKind = "url";
|
|
711
|
+
}
|
|
712
|
+
if (resolved == null && raw.startsWith("/")) {
|
|
713
|
+
resolved = normalizePath(raw);
|
|
714
|
+
resolutionKind = "absolute";
|
|
715
|
+
}
|
|
716
|
+
if (resolved == null && isRelativeSpecifier(raw)) {
|
|
717
|
+
if (!importer) resolved = normalizePath(`/${raw}`);
|
|
718
|
+
else if (isAbsoluteUrl(importer)) resolved = new URL(raw, importer).href;
|
|
719
|
+
else resolved = normalizePath(`${dirname(importer)}/${raw}`);
|
|
720
|
+
resolutionKind = "relative";
|
|
721
|
+
}
|
|
722
|
+
if (resolved == null) {
|
|
723
|
+
resolved = raw;
|
|
724
|
+
resolutionKind = "native";
|
|
725
|
+
}
|
|
726
|
+
} catch (error) {
|
|
727
|
+
throw error;
|
|
621
728
|
}
|
|
622
729
|
|
|
623
|
-
|
|
730
|
+
emit({
|
|
731
|
+
type: "module-resolved",
|
|
732
|
+
specifier: raw,
|
|
733
|
+
importer: importer ?? null,
|
|
734
|
+
resolvedId: resolved,
|
|
735
|
+
resolutionKind,
|
|
736
|
+
});
|
|
737
|
+
return resolved;
|
|
624
738
|
}
|
|
625
739
|
|
|
626
740
|
function syncDependencyEdges(record, nextDependencies) {
|
|
@@ -642,7 +756,7 @@ export function createRuntime(options = {}) {
|
|
|
642
756
|
}
|
|
643
757
|
|
|
644
758
|
function emit(event) {
|
|
645
|
-
if (
|
|
759
|
+
if (events.size === 0) return;
|
|
646
760
|
const payload = {
|
|
647
761
|
runtimeId,
|
|
648
762
|
timestamp: Date.now(),
|
|
@@ -652,23 +766,13 @@ export function createRuntime(options = {}) {
|
|
|
652
766
|
eventBuffer.push(payload);
|
|
653
767
|
return;
|
|
654
768
|
}
|
|
655
|
-
|
|
769
|
+
events.emit(payload);
|
|
656
770
|
}
|
|
657
771
|
|
|
658
772
|
function flushEventBuffer() {
|
|
659
773
|
if (eventBuffer.length === 0) return;
|
|
660
774
|
const pending = eventBuffer.splice(0, eventBuffer.length);
|
|
661
|
-
for (const event of pending)
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function dispatchEvent(payload) {
|
|
665
|
-
for (const listener of [...subscribers]) {
|
|
666
|
-
try {
|
|
667
|
-
listener(payload);
|
|
668
|
-
} catch (error) {
|
|
669
|
-
globalThis.console?.error?.("solid-tag-runtime lifecycle subscriber error", error);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
775
|
+
for (const event of pending) events.emit(event);
|
|
672
776
|
}
|
|
673
777
|
|
|
674
778
|
function emitError(record, phase, error) {
|