module-tsx 0.0.0 → 0.0.1

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 ADDED
@@ -0,0 +1,39 @@
1
+ # module-tsx
2
+
3
+ Run TypeScript (and React) module directly in the browser.
4
+
5
+ # Usage
6
+
7
+ ```html
8
+ <!-- Load This Library -->
9
+ <script type="module" src="https://esm.sh/module-tsx"></script>
10
+
11
+ <div id="root"></div>
12
+
13
+ <!-- Write Your TypeScript Module -->
14
+ <script type="module-tsx">
15
+ import React from "react"; // <- This will be converted to https://esm.sh/react
16
+ import { createRoot } from "react-dom/client"; // <- This will also be converted automatically
17
+ import App from "./src/App.tsx"; // <- Your TypeScript module will also be compiled on the fly
18
+ import lib from "https://my.cdn.domain"; // <- Direct http import will NOT be compiled
19
+
20
+ const root = document.getElementById("root") as HTMLDivElement; // <- You can write TypeScript directly
21
+ createRoot(root).render(
22
+ <React.StrictMode>
23
+ <App />
24
+ </React.StrictMode>
25
+ // ^ You can also write JSX/TSX directly
26
+ );
27
+ </script>
28
+ <!-- OR -->
29
+ <script type="module-tsx" src="./main.tsx"></script>
30
+ ```
31
+
32
+ You publish the **source code**, and users can run it directly in the browser **without any build step**.
33
+
34
+ ```tsx
35
+ // src/App.tsx
36
+ export default function App() {
37
+ return <div>Hello, module-tsx!</div>;
38
+ }
39
+ ```
@@ -0,0 +1,74 @@
1
+ //#region src/importmap.d.ts
2
+ interface ImportMapData {
3
+ imports?: Record<string, string>;
4
+ scopes?: Record<string, Record<string, string>>;
5
+ }
6
+ //#endregion
7
+ //#region src/module-tsx.d.ts
8
+ interface ModuleTSXConfig {
9
+ /**
10
+ * The base URL to resolve relative module specifiers.
11
+ * This is typically the URL of the main script or the HTML page.
12
+ */
13
+ baseUrl?: string;
14
+ fetch?: (fullURL: string) => Promise<Response>;
15
+ importMap?: ImportMapData;
16
+ /**
17
+ * Given a bare specifier, return a full URL to load the module.
18
+ * This can be used to convert for example import "react" to import "https://esm.sh/react".
19
+ * If not provided, it will default to using "https://esm.sh/" as the base URL for bare specifiers.
20
+ * @default "https://esm.sh/"
21
+ */
22
+ resolveBareSpecifier?: string | ((specifier: string) => string);
23
+ }
24
+ interface ModuleTSXEventMap {
25
+ import: CustomEvent<{
26
+ id: string;
27
+ }>;
28
+ "import:error": CustomEvent<{
29
+ id: string;
30
+ error: any;
31
+ }>;
32
+ transform: CustomEvent<{
33
+ sourceUrl: string;
34
+ }>;
35
+ "transform:error": CustomEvent<{
36
+ sourceUrl: string;
37
+ error: any;
38
+ }>;
39
+ }
40
+ interface IModuleTSX extends EventTarget {
41
+ addEventListener<T extends keyof ModuleTSXEventMap>(type: T, listener: (this: ModuleTSX, ev: ModuleTSXEventMap[T]) => any, options?: boolean | AddEventListenerOptions): void;
42
+ }
43
+ declare class ModuleTSX extends EventTarget implements IModuleTSX {
44
+ readonly baseUrl: string;
45
+ readonly importMap: ImportMapData;
46
+ readonly fetch: (url: string) => Promise<Response>;
47
+ readonly resolveBareSpecifier: (specifier: string) => string;
48
+ private readonly sourceTracker;
49
+ private readonly fetchText;
50
+ constructor(config?: ModuleTSXConfig);
51
+ private emit;
52
+ import(id: string, options?: any): Promise<any>;
53
+ importCode(sourceUrl: string, code: string, options?: any): Promise<any>;
54
+ /** Transform module source code and return a blob URL with the transformed content */
55
+ private transformSourceModule;
56
+ private getLoaderByResourceType;
57
+ private tsxLoader;
58
+ private resolveSpecifier;
59
+ private resolveSpecifiers;
60
+ }
61
+ //#endregion
62
+ //#region src/error.d.ts
63
+ /** Custom error class for module-tsx */
64
+ declare class ModuleTSXError extends Error {
65
+ constructor(message?: string, options?: ErrorOptions);
66
+ }
67
+ //#endregion
68
+ //#region src/index.d.ts
69
+ /**
70
+ * The singleton global instance of ModuleTSX.
71
+ */
72
+ declare const instance: ModuleTSX;
73
+ //#endregion
74
+ export { ModuleTSX, ModuleTSXError, instance };
package/dist/index.js ADDED
@@ -0,0 +1,447 @@
1
+ import ts from "typescript";
2
+
3
+ //#region src/error.ts
4
+ /** Custom error class for module-tsx */
5
+ var ModuleTSXError = class ModuleTSXError extends Error {
6
+ constructor(message, options) {
7
+ super(message, options);
8
+ this.name = "ModuleTSXError";
9
+ if ("captureStackTrace" in Error) Error.captureStackTrace(this, ModuleTSXError);
10
+ }
11
+ };
12
+ /** Log warnings with consistent formatting */
13
+ function warn(message, ...args) {
14
+ console.warn(`[module-tsx] ${message}`, ...args);
15
+ }
16
+
17
+ //#endregion
18
+ //#region src/importmap.ts
19
+ function parseImportMaps() {
20
+ const result = {
21
+ imports: {},
22
+ scopes: {}
23
+ };
24
+ const mappedSpecifiers = /* @__PURE__ */ new Set();
25
+ const scripts = document.querySelectorAll("script[type=\"importmap\"]");
26
+ for (const script of scripts) try {
27
+ const data = JSON.parse(script.textContent || "{}");
28
+ if (data.imports) {
29
+ for (const [specifier, url] of Object.entries(data.imports)) if (!mappedSpecifiers.has(specifier)) {
30
+ result.imports[specifier] = url;
31
+ mappedSpecifiers.add(specifier);
32
+ }
33
+ }
34
+ if (data.scopes) for (const [scope, imports] of Object.entries(data.scopes)) {
35
+ if (!result.scopes[scope]) result.scopes[scope] = {};
36
+ for (const [specifier, url] of Object.entries(imports)) {
37
+ const scopedKey = `${scope}::${specifier}`;
38
+ if (!mappedSpecifiers.has(scopedKey)) {
39
+ result.scopes[scope][specifier] = url;
40
+ mappedSpecifiers.add(scopedKey);
41
+ }
42
+ }
43
+ }
44
+ } catch (error) {
45
+ warn(`Failed to parse importmap script:`, error);
46
+ }
47
+ return result;
48
+ }
49
+ /**
50
+ * Resolve specifier from import maps, checking scopes by specificity then global imports
51
+ */
52
+ function resolveFromImportMap(specifier, importMaps, sourceUrl) {
53
+ if (sourceUrl && importMaps.scopes) {
54
+ const matchingScopes = Object.keys(importMaps.scopes).filter((scope) => sourceUrl.startsWith(scope)).sort((a, b) => b.length - a.length);
55
+ for (const scope of matchingScopes) {
56
+ const scopedImports = importMaps.scopes[scope];
57
+ if (scopedImports && scopedImports[specifier]) return scopedImports[specifier];
58
+ }
59
+ }
60
+ if (importMaps.imports && importMaps.imports[specifier]) return importMaps.imports[specifier];
61
+ }
62
+
63
+ //#endregion
64
+ //#region src/loader.ts
65
+ const cssLoader = (sourceUrl, sourceCode) => {
66
+ return `\
67
+ const style = document.createElement("style");
68
+ style.dataset.href = ${JSON.stringify(sourceUrl)}; // for debugging purposes
69
+ style.textContent = ${JSON.stringify(sourceCode)};
70
+ document.head.appendChild(style);
71
+ `;
72
+ };
73
+ const cssModuleLoader = (sourceUrl, sourceCode) => {
74
+ const pathname = new URL(sourceUrl).pathname;
75
+ const filename = pathname.substring(pathname.lastIndexOf("/") + 1) || "index.css";
76
+ const { map, css } = cssToModule(sourceCode, filename.slice(0, filename.indexOf(".")).replace(/[^a-zA-Z0-9]/g, "_"));
77
+ return `\
78
+ const style = document.createElement("style");
79
+ style.dataset.href = ${JSON.stringify(sourceUrl)}; // for debugging purposes
80
+ style.textContent = ${JSON.stringify(css)};
81
+ document.head.appendChild(style);
82
+ export default ${JSON.stringify(map)};
83
+ `;
84
+ };
85
+ function cssToModule(cssString, prefix) {
86
+ const sheet = new CSSStyleSheet();
87
+ sheet.replaceSync(cssString);
88
+ const jsonMap = {};
89
+ const getHash = (name) => {
90
+ const p = prefix ? `${prefix}_` : "";
91
+ if (!jsonMap[name]) jsonMap[name] = `${p}${name}_${Math.random().toString(36).slice(2, 7)}`;
92
+ return jsonMap[name];
93
+ };
94
+ const processRules = (ruleList) => {
95
+ for (const rule of ruleList) if (rule instanceof CSSStyleRule) rule.selectorText = rule.selectorText.replace(/\.([a-zA-Z_][\w-]*)/g, (_match, className) => {
96
+ return `.${getHash(className)}`;
97
+ });
98
+ else if (rule instanceof CSSGroupingRule) processRules(rule.cssRules);
99
+ };
100
+ processRules(sheet.cssRules);
101
+ return {
102
+ css: Array.from(sheet.cssRules).map((rule) => rule.cssText).join("\n"),
103
+ map: jsonMap
104
+ };
105
+ }
106
+
107
+ //#endregion
108
+ //#region src/network.ts
109
+ async function fetchResponse(input, init) {
110
+ const res = await fetch(input, init);
111
+ if (!res.ok) throw new ModuleTSXError(`Failed to fetch resource ${res.url}: ${res.status}`);
112
+ return res;
113
+ }
114
+
115
+ //#endregion
116
+ //#region src/specifier.ts
117
+ function isBareSpecifier(specifier) {
118
+ if (specifier.match(/^\.*\//)) return false;
119
+ try {
120
+ new URL(specifier);
121
+ return false;
122
+ } catch {
123
+ return true;
124
+ }
125
+ }
126
+ function isRelativeSpecifier(specifier) {
127
+ return specifier.startsWith(".") || specifier.startsWith("/");
128
+ }
129
+ function collectSpecifiers(sourceFile) {
130
+ const set = /* @__PURE__ */ new Set();
131
+ const visit = (node) => {
132
+ const addSpecifier = (literal) => {
133
+ if (literal) set.add(literal.text);
134
+ };
135
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) addSpecifier(node.moduleSpecifier);
136
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
137
+ const arg = node.arguments[0];
138
+ if (arg && ts.isStringLiteral(arg)) addSpecifier(arg);
139
+ }
140
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) addSpecifier(node.moduleSpecifier);
141
+ ts.forEachChild(node, visit);
142
+ };
143
+ visit(sourceFile);
144
+ return set;
145
+ }
146
+ function createRewriteImportTransformer(specifierMap) {
147
+ const rewriteSpecifier = (specifier) => {
148
+ return specifierMap.get(specifier) ?? specifier;
149
+ };
150
+ const transformer = (context) => {
151
+ const visitNode = (node) => {
152
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
153
+ const next = rewriteSpecifier(node.moduleSpecifier.text);
154
+ if (next !== node.moduleSpecifier.text) return ts.factory.updateImportDeclaration(node, node.modifiers, node.importClause, ts.factory.createStringLiteral(next), node.assertClause);
155
+ }
156
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
157
+ const arg = node.arguments[0];
158
+ if (arg && ts.isStringLiteral(arg)) {
159
+ const next = rewriteSpecifier(arg.text);
160
+ if (next !== arg.text) return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ts.factory.createStringLiteral(next), ...node.arguments.slice(1)]);
161
+ }
162
+ }
163
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
164
+ const next = rewriteSpecifier(node.moduleSpecifier.text);
165
+ if (next !== node.moduleSpecifier.text) return ts.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, ts.factory.createStringLiteral(next), node.assertClause);
166
+ }
167
+ return ts.visitEachChild(node, visitNode, context);
168
+ };
169
+ return (sf) => ts.visitNode(sf, visitNode);
170
+ };
171
+ return transformer;
172
+ }
173
+
174
+ //#endregion
175
+ //#region src/react.ts
176
+ /** Check if code uses JSX without React import */
177
+ function needsReactImport(sourceFile) {
178
+ let hasJSX = false;
179
+ let hasReactVariable = false;
180
+ function visitNode(node) {
181
+ if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) hasJSX = true;
182
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
183
+ if (node.importClause) {
184
+ if (node.importClause.name?.text === "React") hasReactVariable = true;
185
+ if (node.importClause.namedBindings && ts.isNamespaceImport(node.importClause.namedBindings)) {
186
+ if (node.importClause.namedBindings.name.text === "React") hasReactVariable = true;
187
+ }
188
+ }
189
+ }
190
+ if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
191
+ if (node.name.text === "React") hasReactVariable = true;
192
+ }
193
+ ts.forEachChild(node, visitNode);
194
+ }
195
+ visitNode(sourceFile);
196
+ return hasJSX && !hasReactVariable;
197
+ }
198
+ /** Add React import statement to the top */
199
+ function addReactImport(sourceFile) {
200
+ let reactSpecifier = "react";
201
+ function findReactSpecifier(node) {
202
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
203
+ const specifier = node.moduleSpecifier.text;
204
+ if (specifier === "react" || /^react@/.test(specifier) || specifier.endsWith("/react") || /\/react@/.test(specifier)) {
205
+ reactSpecifier = specifier;
206
+ return;
207
+ }
208
+ }
209
+ ts.forEachChild(node, findReactSpecifier);
210
+ }
211
+ findReactSpecifier(sourceFile);
212
+ const statements = [ts.factory.createImportDeclaration(void 0, ts.factory.createImportClause(false, ts.factory.createIdentifier("React"), void 0), ts.factory.createStringLiteral(reactSpecifier), void 0), ...sourceFile.statements];
213
+ return ts.factory.updateSourceFile(sourceFile, statements, sourceFile.isDeclarationFile, sourceFile.referencedFiles, sourceFile.typeReferenceDirectives, sourceFile.hasNoDefaultLib, sourceFile.libReferenceDirectives);
214
+ }
215
+
216
+ //#endregion
217
+ //#region src/source-tracker.ts
218
+ var SourceTransformTracker = class {
219
+ sourceMap = /* @__PURE__ */ new Map();
220
+ inFlightSourceMap = /* @__PURE__ */ new Map();
221
+ blobMap = /* @__PURE__ */ new Map();
222
+ get(sourceType, sourceUrl) {
223
+ return this.sourceMap.get(this.getSourceKey(sourceType, sourceUrl));
224
+ }
225
+ set(sourceType, sourceUrl, blobUrl) {
226
+ this.sourceMap.set(this.getSourceKey(sourceType, sourceUrl), blobUrl);
227
+ this.blobMap.set(blobUrl, sourceUrl);
228
+ }
229
+ isInFlight(sourceType, sourceUrl) {
230
+ return this.inFlightSourceMap.has(this.getSourceKey(sourceType, sourceUrl));
231
+ }
232
+ getSourceUrlByBlob(blobUrl) {
233
+ return this.blobMap.get(blobUrl);
234
+ }
235
+ /** Deduplicates concurrent transforms for the same URL. */
236
+ runWithDedup(sourceType, sourceUrl, run) {
237
+ const sourceKey = this.getSourceKey(sourceType, sourceUrl);
238
+ const inFlight = this.inFlightSourceMap.get(sourceKey);
239
+ if (inFlight) return inFlight;
240
+ const task = run();
241
+ this.inFlightSourceMap.set(sourceKey, task);
242
+ task.finally(() => {
243
+ this.inFlightSourceMap.delete(sourceKey);
244
+ });
245
+ return task;
246
+ }
247
+ getSourceKey(sourceType, sourceUrl) {
248
+ return `${sourceType}:${sourceUrl}`;
249
+ }
250
+ };
251
+
252
+ //#endregion
253
+ //#region src/ts.ts
254
+ function createSourceFile(code, fileName) {
255
+ try {
256
+ return ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
257
+ } catch (cause) {
258
+ throw new ModuleTSXError(`Failed to create typescript source file ${fileName}`, { cause });
259
+ }
260
+ }
261
+ function printSourceFile(sourceFile) {
262
+ try {
263
+ const code = ts.createPrinter({
264
+ newLine: ts.NewLineKind.LineFeed,
265
+ removeComments: false
266
+ }).printFile(sourceFile);
267
+ return ts.transpile(code, {
268
+ target: ts.ScriptTarget.Latest,
269
+ module: ts.ModuleKind.ESNext,
270
+ noCheck: true,
271
+ declaration: false,
272
+ jsx: ts.JsxEmit.React
273
+ });
274
+ } catch (cause) {
275
+ throw new ModuleTSXError(`Failed to print typescript source file ${sourceFile.fileName}`, { cause });
276
+ }
277
+ }
278
+ function transform(sourceFile, transformers) {
279
+ try {
280
+ const result = ts.transform(sourceFile, transformers);
281
+ const transformedFile = result.transformed[0];
282
+ result.dispose();
283
+ return transformedFile;
284
+ } catch (cause) {
285
+ throw new ModuleTSXError(`Failed to transform typescript source file ${sourceFile.fileName}`, { cause });
286
+ }
287
+ }
288
+
289
+ //#endregion
290
+ //#region src/module-tsx.ts
291
+ var ModuleTSX = class extends EventTarget {
292
+ baseUrl;
293
+ importMap;
294
+ fetch;
295
+ resolveBareSpecifier;
296
+ sourceTracker = new SourceTransformTracker();
297
+ fetchText = async (url) => {
298
+ return this.fetch(url).then((res) => res.text());
299
+ };
300
+ constructor(config) {
301
+ super();
302
+ this.baseUrl = config?.baseUrl ?? location.href;
303
+ this.importMap = config?.importMap ?? parseImportMaps();
304
+ this.fetch = config?.fetch ?? fetchResponse;
305
+ this.resolveBareSpecifier = typeof config?.resolveBareSpecifier === "function" ? config?.resolveBareSpecifier : (specifier) => (config?.resolveBareSpecifier ?? "https://esm.sh/") + specifier;
306
+ }
307
+ emit(type, detail) {
308
+ this.dispatchEvent(new CustomEvent(type, { detail }));
309
+ this.dispatchEvent(new CustomEvent("*", { detail: {
310
+ type,
311
+ payload: detail
312
+ } }));
313
+ }
314
+ async import(id, options) {
315
+ this.emit("import", { id });
316
+ try {
317
+ if (isBareSpecifier(id)) {
318
+ const mappedSpecifier = resolveFromImportMap(id, this.importMap, this.baseUrl);
319
+ if (mappedSpecifier) id = mappedSpecifier;
320
+ else id = this.resolveBareSpecifier(id);
321
+ }
322
+ const url = isRelativeSpecifier(id) ? new URL(id, this.baseUrl).href : id;
323
+ const code = await this.fetchText(url);
324
+ return this.importCode(url, code, options);
325
+ } catch (error) {
326
+ this.emit("import:error", {
327
+ id,
328
+ error
329
+ });
330
+ throw error;
331
+ }
332
+ }
333
+ async importCode(sourceUrl, code, options) {
334
+ return await import(await this.transformSourceModule("esm", sourceUrl, code), options);
335
+ }
336
+ /** Transform module source code and return a blob URL with the transformed content */
337
+ async transformSourceModule(sourceType, sourceUrl, sourceCode) {
338
+ const cachedBlobUrl = this.sourceTracker.get(sourceType, sourceUrl);
339
+ if (cachedBlobUrl) return cachedBlobUrl;
340
+ return this.sourceTracker.runWithDedup(sourceType, sourceUrl, async () => {
341
+ const loader = this.getLoaderByResourceType(sourceType);
342
+ const code = `import.meta.url=${JSON.stringify(sourceUrl)};\n` + await loader(sourceUrl, sourceCode);
343
+ const blob = new Blob([code], { type: "text/javascript" });
344
+ const blobUrl = URL.createObjectURL(blob);
345
+ this.sourceTracker.set(sourceType, sourceUrl, blobUrl);
346
+ return blobUrl;
347
+ });
348
+ }
349
+ getLoaderByResourceType(type) {
350
+ switch (type) {
351
+ case "css": return cssLoader;
352
+ case "css-module": return cssModuleLoader;
353
+ case "esm": return this.tsxLoader.bind(this);
354
+ default: throw new ModuleTSXError(`Unsupported resource type: ${type}`);
355
+ }
356
+ }
357
+ async tsxLoader(sourceUrl, sourceCode) {
358
+ this.emit("transform", { sourceUrl });
359
+ try {
360
+ const sourceFile = createSourceFile(sourceCode, getFileName(sourceUrl));
361
+ const specifiers = collectSpecifiers(sourceFile);
362
+ const rewrittenSpecifiers = await this.resolveSpecifiers(specifiers, sourceUrl);
363
+ let workingSourceFile = sourceFile;
364
+ if (needsReactImport(workingSourceFile)) workingSourceFile = addReactImport(workingSourceFile);
365
+ const transformers = [createRewriteImportTransformer(rewrittenSpecifiers)];
366
+ return printSourceFile(transform(workingSourceFile, transformers));
367
+ } catch (error) {
368
+ this.emit("transform:error", {
369
+ sourceUrl,
370
+ error
371
+ });
372
+ throw error;
373
+ }
374
+ }
375
+ async resolveSpecifier(specifier, sourceUrl) {
376
+ const mappedSpecifier = resolveFromImportMap(specifier, this.importMap, sourceUrl);
377
+ if (mappedSpecifier) return mappedSpecifier;
378
+ const getCssUrl = async (fullURL) => {
379
+ return await this.transformSourceModule("css", fullURL, await this.fetchText(fullURL));
380
+ };
381
+ const toCDNUrl = (specifier) => {
382
+ const subpath = specifier.startsWith("@") ? specifier.split("/").slice(2).join("/") : specifier.split("/").slice(1).join("/");
383
+ const url = this.resolveBareSpecifier(specifier);
384
+ if (subpath.endsWith(".css")) return getCssUrl(url);
385
+ return url;
386
+ };
387
+ if (isRelativeSpecifier(specifier)) {
388
+ const targetUrl = new URL(specifier, sourceUrl);
389
+ if (targetUrl.pathname.endsWith(".module.css")) return await this.transformSourceModule("css-module", targetUrl.href, await this.fetchText(targetUrl.href));
390
+ else if (targetUrl.pathname.endsWith(".css")) return getCssUrl(targetUrl.href);
391
+ else if (targetUrl.pathname.endsWith(".wasm")) return targetUrl.href;
392
+ else {
393
+ if (this.sourceTracker.isInFlight("esm", targetUrl.href)) return targetUrl.href;
394
+ //! ^ transformSourceModule is recursive ^
395
+ return await this.transformSourceModule("esm", targetUrl.href, await this.fetchText(targetUrl.href));
396
+ }
397
+ } else if (specifier.startsWith("node:")) return `https://raw.esm.sh/@jspm/core/nodelibs/browser/${specifier.slice(5)}.js`;
398
+ else if (specifier.startsWith("npm:")) return toCDNUrl(specifier.slice(4));
399
+ else if (isBareSpecifier(specifier)) return toCDNUrl(specifier);
400
+ else return specifier;
401
+ }
402
+ async resolveSpecifiers(specifiers, sourceUrl) {
403
+ const resolved = /* @__PURE__ */ new Map();
404
+ const tasks = Array.from(specifiers).map(async (specifier) => {
405
+ const specifier2 = await this.resolveSpecifier(specifier, sourceUrl);
406
+ if (specifier !== specifier2) resolved.set(specifier, specifier2);
407
+ });
408
+ await Promise.all(tasks);
409
+ return resolved;
410
+ }
411
+ };
412
+ function getFileName(sourceUrl) {
413
+ try {
414
+ return new URL(sourceUrl).pathname;
415
+ } catch {
416
+ return "temp.tsx";
417
+ }
418
+ }
419
+
420
+ //#endregion
421
+ //#region src/index.ts
422
+ /**
423
+ * The singleton global instance of ModuleTSX.
424
+ */
425
+ const instance = new ModuleTSX();
426
+ const TYPE_ATTRIBUTE_VALUE = "module-tsx";
427
+ async function sideEffect() {
428
+ const importScript = async (script) => {
429
+ const src = script.src;
430
+ if (src) return instance.import(src);
431
+ else {
432
+ const code = script.innerHTML || "";
433
+ return instance.importCode(document.location.href, code);
434
+ }
435
+ };
436
+ for (const s of Array.from(document.querySelectorAll(`script[type="${TYPE_ATTRIBUTE_VALUE}"]`))) {
437
+ const script = s;
438
+ if (!script.async && script.defer) warn(`script with type="${TYPE_ATTRIBUTE_VALUE}" does not support defer attribute. Use async or no attribute instead.`);
439
+ for (const key in ["integrity", "crossorigin"]) if (script[key]) warn(`script with type="${TYPE_ATTRIBUTE_VALUE}" does not support ${key} attribute.`);
440
+ if (script.async) importScript(script);
441
+ else await importScript(script);
442
+ }
443
+ }
444
+ document.addEventListener("DOMContentLoaded", sideEffect);
445
+
446
+ //#endregion
447
+ export { ModuleTSX, ModuleTSXError, instance };