solid-tag-runtime 0.0.6 → 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 +167 -3
- package/README.md +233 -3
- package/html.d.ts +79 -1
- package/index.d.ts +71 -0
- package/package.json +1 -1
- package/src/events.js +63 -0
- package/src/html.js +420 -23
- package/src/runtime.js +392 -78
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,6 +28,13 @@ export function createRuntime(options = {}) {
|
|
|
27
28
|
const records = new Map();
|
|
28
29
|
let anonymousSequence = 0;
|
|
29
30
|
let disposed = false;
|
|
31
|
+
const events = createEventDispatcher({
|
|
32
|
+
onListenerError(error) {
|
|
33
|
+
globalThis.console?.error?.("solid-tag-runtime lifecycle subscriber error", error);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
let eventBufferDepth = 0;
|
|
37
|
+
const eventBuffer = [];
|
|
30
38
|
|
|
31
39
|
const globalRegistry = getHostRegistry();
|
|
32
40
|
const hostNamespaces = new Map();
|
|
@@ -34,6 +42,7 @@ export function createRuntime(options = {}) {
|
|
|
34
42
|
|
|
35
43
|
const api = {
|
|
36
44
|
define,
|
|
45
|
+
defineMany,
|
|
37
46
|
defineModule,
|
|
38
47
|
defineUrl,
|
|
39
48
|
update,
|
|
@@ -43,6 +52,9 @@ export function createRuntime(options = {}) {
|
|
|
43
52
|
toComponent,
|
|
44
53
|
has,
|
|
45
54
|
invalidate,
|
|
55
|
+
remove,
|
|
56
|
+
clear,
|
|
57
|
+
subscribe,
|
|
46
58
|
modules: listModules,
|
|
47
59
|
dependencies,
|
|
48
60
|
dependents,
|
|
@@ -61,11 +73,63 @@ export function createRuntime(options = {}) {
|
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
function define(id, source, defineOptions = {}) {
|
|
76
|
+
return defineSource(id, source, defineOptions, "define");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function defineMany(definitions) {
|
|
80
|
+
assertActive();
|
|
81
|
+
if (!Array.isArray(definitions)) {
|
|
82
|
+
throw new TypeError("defineMany(definitions) expects an array of source module definitions.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const prepared = definitions.map((definition, index) => {
|
|
86
|
+
if (!definition || typeof definition !== "object") {
|
|
87
|
+
throw new TypeError(`defineMany() item ${index} must be an object with id and source.`);
|
|
88
|
+
}
|
|
89
|
+
if (definition.id == null) {
|
|
90
|
+
throw new TypeError(`defineMany() item ${index} is missing id.`);
|
|
91
|
+
}
|
|
92
|
+
const id = normalizeDefinedId(definition.id);
|
|
93
|
+
return {
|
|
94
|
+
id,
|
|
95
|
+
source: String(definition.source ?? ""),
|
|
96
|
+
format: definition.format ?? "jsx",
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
for (const item of prepared) {
|
|
102
|
+
if (seen.has(item.id)) {
|
|
103
|
+
throw new SolidTagRuntimeError(`defineMany() contains duplicate module id ${JSON.stringify(item.id)}.`);
|
|
104
|
+
}
|
|
105
|
+
seen.add(item.id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
eventBufferDepth += 1;
|
|
109
|
+
try {
|
|
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;
|
|
117
|
+
} finally {
|
|
118
|
+
eventBufferDepth -= 1;
|
|
119
|
+
if (eventBufferDepth === 0) flushEventBuffer();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function update(id, source, updateOptions = {}) {
|
|
124
|
+
return defineSource(id, source, updateOptions, "update");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function defineSource(id, source, defineOptions = {}, operation = "define") {
|
|
64
128
|
assertActive();
|
|
65
129
|
const normalized = normalizeDefinedId(id);
|
|
66
130
|
const existing = records.get(normalized);
|
|
67
131
|
|
|
68
|
-
if (existing) invalidate(normalized, { dependents: true });
|
|
132
|
+
if (existing) invalidate(normalized, { dependents: true, reason: operation });
|
|
69
133
|
|
|
70
134
|
const record = {
|
|
71
135
|
id: normalized,
|
|
@@ -84,18 +148,34 @@ export function createRuntime(options = {}) {
|
|
|
84
148
|
};
|
|
85
149
|
|
|
86
150
|
records.set(normalized, record);
|
|
151
|
+
emit({
|
|
152
|
+
type: "module-defined",
|
|
153
|
+
id: normalized,
|
|
154
|
+
kind: record.kind,
|
|
155
|
+
version: record.version,
|
|
156
|
+
operation,
|
|
157
|
+
replaced: Boolean(existing),
|
|
158
|
+
module: toModuleInfo(record),
|
|
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
|
+
}
|
|
87
171
|
return normalized;
|
|
88
172
|
}
|
|
89
173
|
|
|
90
|
-
function update(id, source, updateOptions = {}) {
|
|
91
|
-
return define(id, source, updateOptions);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
174
|
function defineModule(id, namespace) {
|
|
95
175
|
assertActive();
|
|
96
176
|
const normalized = normalizeDefinedId(id);
|
|
97
177
|
const existing = records.get(normalized);
|
|
98
|
-
if (existing) invalidate(normalized, { dependents: true });
|
|
178
|
+
if (existing) invalidate(normalized, { dependents: true, reason: "defineModule" });
|
|
99
179
|
|
|
100
180
|
if ((typeof namespace !== "object" || namespace === null) && typeof namespace !== "function") {
|
|
101
181
|
throw new TypeError("defineModule(id, namespace) expects an object, function, or ES module namespace-like value.");
|
|
@@ -103,7 +183,7 @@ export function createRuntime(options = {}) {
|
|
|
103
183
|
|
|
104
184
|
hostNamespaces.set(normalized, namespace);
|
|
105
185
|
|
|
106
|
-
|
|
186
|
+
const record = {
|
|
107
187
|
id: normalized,
|
|
108
188
|
kind: "host",
|
|
109
189
|
namespace,
|
|
@@ -112,6 +192,16 @@ export function createRuntime(options = {}) {
|
|
|
112
192
|
dependencies: new Set(),
|
|
113
193
|
dependents: existing?.dependents ?? new Set(),
|
|
114
194
|
url: undefined,
|
|
195
|
+
};
|
|
196
|
+
records.set(normalized, record);
|
|
197
|
+
emit({
|
|
198
|
+
type: "module-defined",
|
|
199
|
+
id: normalized,
|
|
200
|
+
kind: record.kind,
|
|
201
|
+
version: record.version,
|
|
202
|
+
operation: "defineModule",
|
|
203
|
+
replaced: Boolean(existing),
|
|
204
|
+
module: toModuleInfo(record),
|
|
115
205
|
});
|
|
116
206
|
|
|
117
207
|
return normalized;
|
|
@@ -121,10 +211,10 @@ export function createRuntime(options = {}) {
|
|
|
121
211
|
assertActive();
|
|
122
212
|
const normalized = normalizeDefinedId(id);
|
|
123
213
|
const existing = records.get(normalized);
|
|
124
|
-
if (existing) invalidate(normalized, { dependents: true });
|
|
214
|
+
if (existing) invalidate(normalized, { dependents: true, reason: "defineUrl" });
|
|
125
215
|
|
|
126
216
|
const href = String(url);
|
|
127
|
-
|
|
217
|
+
const record = {
|
|
128
218
|
id: normalized,
|
|
129
219
|
kind: "url",
|
|
130
220
|
externalUrl: href,
|
|
@@ -134,6 +224,16 @@ export function createRuntime(options = {}) {
|
|
|
134
224
|
dependents: existing?.dependents ?? new Set(),
|
|
135
225
|
importPromise: undefined,
|
|
136
226
|
namespace: undefined,
|
|
227
|
+
};
|
|
228
|
+
records.set(normalized, record);
|
|
229
|
+
emit({
|
|
230
|
+
type: "module-defined",
|
|
231
|
+
id: normalized,
|
|
232
|
+
kind: record.kind,
|
|
233
|
+
version: record.version,
|
|
234
|
+
operation: "defineUrl",
|
|
235
|
+
replaced: Boolean(existing),
|
|
236
|
+
module: toModuleInfo(record),
|
|
137
237
|
});
|
|
138
238
|
|
|
139
239
|
return normalized;
|
|
@@ -155,23 +255,38 @@ export function createRuntime(options = {}) {
|
|
|
155
255
|
|
|
156
256
|
if (record.kind === "url") {
|
|
157
257
|
if (!record.importPromise) {
|
|
258
|
+
record.state = "evaluating";
|
|
259
|
+
emit({ type: "module-evaluating", id: record.id, kind: record.kind, version: record.version });
|
|
158
260
|
record.importPromise = import(record.externalUrl).then(namespace => {
|
|
159
261
|
record.namespace = namespace;
|
|
160
262
|
record.state = "ready";
|
|
263
|
+
emit({ type: "module-evaluated", id: record.id, kind: record.kind, version: record.version, module: toModuleInfo(record) });
|
|
161
264
|
return namespace;
|
|
265
|
+
}).catch(error => {
|
|
266
|
+
record.importPromise = undefined;
|
|
267
|
+
record.state = "error";
|
|
268
|
+
emitError(record, "evaluate", error);
|
|
269
|
+
throw error;
|
|
162
270
|
});
|
|
163
271
|
}
|
|
164
272
|
return record.importPromise;
|
|
165
273
|
}
|
|
166
274
|
|
|
167
275
|
if (!record.importPromise) {
|
|
168
|
-
record.importPromise = ensureSourceUrl(record, []).then(url =>
|
|
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 => {
|
|
169
281
|
record.namespace = namespace;
|
|
170
282
|
record.state = "ready";
|
|
283
|
+
emit({ type: "module-evaluated", id: record.id, kind: record.kind, version: record.version, module: toModuleInfo(record) });
|
|
171
284
|
return namespace;
|
|
172
285
|
}).catch(error => {
|
|
173
286
|
record.importPromise = undefined;
|
|
287
|
+
const alreadyReported = record.state === "error";
|
|
174
288
|
record.state = "error";
|
|
289
|
+
if (!alreadyReported) emitError(record, "evaluate", error);
|
|
175
290
|
throw error;
|
|
176
291
|
});
|
|
177
292
|
}
|
|
@@ -241,6 +356,7 @@ export function createRuntime(options = {}) {
|
|
|
241
356
|
assertActive();
|
|
242
357
|
const normalized = normalizeLookupId(id);
|
|
243
358
|
const includeDependents = invalidateOptions.dependents ?? true;
|
|
359
|
+
const reason = invalidateOptions.reason ?? "explicit";
|
|
244
360
|
const seen = new Set();
|
|
245
361
|
|
|
246
362
|
invalidateOne(normalized);
|
|
@@ -271,7 +387,67 @@ export function createRuntime(options = {}) {
|
|
|
271
387
|
record.namespace = undefined;
|
|
272
388
|
record.state = "defined";
|
|
273
389
|
}
|
|
390
|
+
|
|
391
|
+
emit({
|
|
392
|
+
type: "module-invalidated",
|
|
393
|
+
id: record.id,
|
|
394
|
+
kind: record.kind,
|
|
395
|
+
version: record.version,
|
|
396
|
+
reason,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function remove(id, removeOptions = {}) {
|
|
402
|
+
assertActive();
|
|
403
|
+
const normalized = normalizeLookupId(id);
|
|
404
|
+
const record = records.get(normalized);
|
|
405
|
+
if (!record) return false;
|
|
406
|
+
|
|
407
|
+
if (removeOptions.invalidateDependents !== false) {
|
|
408
|
+
for (const dependent of [...record.dependents]) {
|
|
409
|
+
invalidate(dependent, { dependents: true, reason: `dependency-removed:${normalized}` });
|
|
410
|
+
}
|
|
274
411
|
}
|
|
412
|
+
|
|
413
|
+
for (const dependency of record.dependencies) {
|
|
414
|
+
records.get(dependency)?.dependents.delete(normalized);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
revokeRecordUrl(record);
|
|
418
|
+
if (record.kind === "host") hostNamespaces.delete(normalized);
|
|
419
|
+
records.delete(normalized);
|
|
420
|
+
|
|
421
|
+
emit({
|
|
422
|
+
type: "module-removed",
|
|
423
|
+
id: normalized,
|
|
424
|
+
kind: record.kind,
|
|
425
|
+
version: record.version,
|
|
426
|
+
invalidateDependents: removeOptions.invalidateDependents !== false,
|
|
427
|
+
});
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function clear(clearOptions = {}) {
|
|
432
|
+
assertActive();
|
|
433
|
+
const preserveHostModules = clearOptions.preserveHostModules !== false;
|
|
434
|
+
const ids = [...records.values()]
|
|
435
|
+
.filter(record => !(preserveHostModules && record.kind === "host"))
|
|
436
|
+
.map(record => record.id);
|
|
437
|
+
|
|
438
|
+
for (const id of ids) remove(id, { invalidateDependents: false });
|
|
439
|
+
|
|
440
|
+
emit({
|
|
441
|
+
type: "runtime-cleared",
|
|
442
|
+
ids: [...ids],
|
|
443
|
+
preserveHostModules,
|
|
444
|
+
});
|
|
445
|
+
return ids;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function subscribe(typesOrListener, maybeListener) {
|
|
449
|
+
assertActive();
|
|
450
|
+
return events.subscribe(typesOrListener, maybeListener);
|
|
275
451
|
}
|
|
276
452
|
|
|
277
453
|
function listModules() {
|
|
@@ -296,11 +472,13 @@ export function createRuntime(options = {}) {
|
|
|
296
472
|
function dispose() {
|
|
297
473
|
if (disposed) return;
|
|
298
474
|
disposed = true;
|
|
475
|
+
emit({ type: "runtime-disposed" });
|
|
299
476
|
|
|
300
477
|
for (const record of records.values()) revokeRecordUrl(record);
|
|
301
478
|
records.clear();
|
|
302
479
|
hostNamespaces.clear();
|
|
303
480
|
globalRegistry.delete(runtimeId);
|
|
481
|
+
events.clear();
|
|
304
482
|
}
|
|
305
483
|
|
|
306
484
|
function resolvePublic(specifier, importer) {
|
|
@@ -312,93 +490,159 @@ export function createRuntime(options = {}) {
|
|
|
312
490
|
|
|
313
491
|
if (stack.includes(record.id)) {
|
|
314
492
|
const cycleStart = stack.indexOf(record.id);
|
|
315
|
-
|
|
493
|
+
const error = new CircularDependencyError([...stack.slice(cycleStart), record.id]);
|
|
494
|
+
emitError(record, "link", error);
|
|
495
|
+
throw error;
|
|
316
496
|
}
|
|
317
497
|
|
|
318
498
|
const nextStack = [...stack, record.id];
|
|
319
499
|
record.state = "linking";
|
|
500
|
+
emit({
|
|
501
|
+
type: "module-linking",
|
|
502
|
+
id: record.id,
|
|
503
|
+
kind: record.kind,
|
|
504
|
+
version: record.version,
|
|
505
|
+
});
|
|
320
506
|
|
|
321
507
|
let analysis;
|
|
322
508
|
try {
|
|
323
509
|
analysis = compiler.analyze(record.source, { id: record.id, format: record.format });
|
|
324
510
|
} catch (error) {
|
|
325
511
|
record.state = "error";
|
|
326
|
-
|
|
512
|
+
const wrapped = new ModuleCompileError(record.id, error);
|
|
513
|
+
emitError(record, "analyze", wrapped);
|
|
514
|
+
throw wrapped;
|
|
327
515
|
}
|
|
328
516
|
|
|
329
517
|
const resolvedImports = [];
|
|
518
|
+
const linkedDependencies = [];
|
|
330
519
|
const nextDependencies = new Set();
|
|
331
520
|
|
|
332
521
|
for (const reference of analysis.imports ?? []) {
|
|
333
|
-
|
|
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
|
+
|
|
334
531
|
const dependencyRecord = records.get(resolved);
|
|
335
532
|
let replacement = resolved;
|
|
336
533
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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);
|
|
346
549
|
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
throw
|
|
550
|
+
} catch (error) {
|
|
551
|
+
record.state = "error";
|
|
552
|
+
emitError(record, error instanceof ModuleResolutionError ? "resolve" : "link", error);
|
|
553
|
+
throw error;
|
|
351
554
|
}
|
|
352
555
|
|
|
353
556
|
resolvedImports.push({ ...reference, replacement });
|
|
557
|
+
linkedDependencies.push({
|
|
558
|
+
specifier: reference.specifier,
|
|
559
|
+
resolvedId: dependencyRecord?.id ?? resolved,
|
|
560
|
+
});
|
|
354
561
|
}
|
|
355
562
|
|
|
356
|
-
let linkedSource
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
? await
|
|
372
|
-
: htmlRecord.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
});
|
|
377
592
|
}
|
|
378
|
-
}
|
|
379
593
|
|
|
380
|
-
|
|
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
|
+
});
|
|
381
603
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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;
|
|
394
622
|
}
|
|
395
623
|
} catch (error) {
|
|
396
|
-
record.state
|
|
397
|
-
|
|
624
|
+
if (record.state !== "error") {
|
|
625
|
+
record.state = "error";
|
|
626
|
+
emitError(record, error instanceof ModuleResolutionError ? "resolve" : "link", error);
|
|
627
|
+
}
|
|
628
|
+
throw error;
|
|
398
629
|
}
|
|
399
630
|
|
|
400
|
-
|
|
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
|
+
}
|
|
401
638
|
record.state = "compiled";
|
|
639
|
+
emit({
|
|
640
|
+
type: "module-compiled",
|
|
641
|
+
id: record.id,
|
|
642
|
+
kind: record.kind,
|
|
643
|
+
version: record.version,
|
|
644
|
+
module: toModuleInfo(record),
|
|
645
|
+
});
|
|
402
646
|
return record.url;
|
|
403
647
|
}
|
|
404
648
|
|
|
@@ -428,30 +672,69 @@ export function createRuntime(options = {}) {
|
|
|
428
672
|
);
|
|
429
673
|
}
|
|
430
674
|
|
|
431
|
-
|
|
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
|
+
}
|
|
432
681
|
return record.url;
|
|
433
682
|
}
|
|
434
683
|
|
|
435
684
|
function resolveSpecifier(specifier, importer) {
|
|
436
685
|
const raw = String(specifier);
|
|
686
|
+
emit({
|
|
687
|
+
type: "module-resolving",
|
|
688
|
+
specifier: raw,
|
|
689
|
+
importer: importer ?? null,
|
|
690
|
+
});
|
|
437
691
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (typeof custom === "string" && custom) return normalizeResolved(custom, importer);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (records.has(raw)) return raw;
|
|
444
|
-
if (isAbsoluteUrl(raw)) return raw;
|
|
692
|
+
let resolved;
|
|
693
|
+
let resolutionKind;
|
|
445
694
|
|
|
446
|
-
|
|
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
|
+
}
|
|
447
703
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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;
|
|
452
728
|
}
|
|
453
729
|
|
|
454
|
-
|
|
730
|
+
emit({
|
|
731
|
+
type: "module-resolved",
|
|
732
|
+
specifier: raw,
|
|
733
|
+
importer: importer ?? null,
|
|
734
|
+
resolvedId: resolved,
|
|
735
|
+
resolutionKind,
|
|
736
|
+
});
|
|
737
|
+
return resolved;
|
|
455
738
|
}
|
|
456
739
|
|
|
457
740
|
function syncDependencyEdges(record, nextDependencies) {
|
|
@@ -472,6 +755,37 @@ export function createRuntime(options = {}) {
|
|
|
472
755
|
}
|
|
473
756
|
}
|
|
474
757
|
|
|
758
|
+
function emit(event) {
|
|
759
|
+
if (events.size === 0) return;
|
|
760
|
+
const payload = {
|
|
761
|
+
runtimeId,
|
|
762
|
+
timestamp: Date.now(),
|
|
763
|
+
...event,
|
|
764
|
+
};
|
|
765
|
+
if (eventBufferDepth > 0) {
|
|
766
|
+
eventBuffer.push(payload);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
events.emit(payload);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function flushEventBuffer() {
|
|
773
|
+
if (eventBuffer.length === 0) return;
|
|
774
|
+
const pending = eventBuffer.splice(0, eventBuffer.length);
|
|
775
|
+
for (const event of pending) events.emit(event);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function emitError(record, phase, error) {
|
|
779
|
+
emit({
|
|
780
|
+
type: "module-error",
|
|
781
|
+
id: record?.id,
|
|
782
|
+
kind: record?.kind,
|
|
783
|
+
version: record?.version,
|
|
784
|
+
phase,
|
|
785
|
+
error,
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
475
789
|
function toModuleInfo(record) {
|
|
476
790
|
return {
|
|
477
791
|
id: record.id,
|