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/dist/index.mjs CHANGED
@@ -1,76 +1,179 @@
1
- import ts from "typescript";
1
+ import ts from "https://esm.sh/typescript@5.9.3";
2
2
 
3
- //#region \0@oxc-project+runtime@0.110.0/helpers/asyncToGenerator.js
4
- function asyncGeneratorStep(n, t, e, r, o, a, c) {
5
- try {
6
- var i = n[a](c), u = i.value;
7
- } catch (n) {
8
- e(n);
9
- return;
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
10
  }
11
- i.done ? t(u) : Promise.resolve(u).then(r, o);
11
+ };
12
+ /** Log warnings with consistent formatting */
13
+ function warn(message, ...args) {
14
+ console.warn(`[module-tsx] ${message}`, ...args);
12
15
  }
13
- function _asyncToGenerator(n) {
14
- return function() {
15
- var t = this, e = arguments;
16
- return new Promise(function(r, o) {
17
- var a = n.apply(t, e);
18
- function _next(n) {
19
- asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
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);
20
32
  }
21
- function _throw(n) {
22
- asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
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
+ }
23
42
  }
24
- _next(void 0);
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)}`;
25
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
26
104
  };
27
105
  }
28
106
 
29
107
  //#endregion
30
108
  //#region src/network.ts
31
- function fetchModule(_x, _x2) {
32
- return _fetchModule.apply(this, arguments);
33
- }
34
- function _fetchModule() {
35
- _fetchModule = _asyncToGenerator(function* (input, init) {
36
- const res = yield fetch(input, init);
37
- if (!res.ok) throw new Error(`Failed to fetch module ${res.url}: ${res.status}`);
38
- return res.text();
39
- });
40
- return _fetchModule.apply(this, arguments);
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;
41
113
  }
42
114
 
43
115
  //#endregion
44
- //#region src/ts.ts
45
- const FILE_NAME = "temp.tsx";
46
- function createSourceFile(code, fileName = FILE_NAME) {
47
- return ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
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
+ }
48
125
  }
49
- function printSourceFile(sourceFile) {
50
- const code = ts.createPrinter({
51
- newLine: ts.NewLineKind.LineFeed,
52
- removeComments: false
53
- }).printFile(sourceFile);
54
- return ts.transpile(code, {
55
- target: ts.ScriptTarget.Latest,
56
- module: ts.ModuleKind.ESNext,
57
- noCheck: true,
58
- declaration: false,
59
- jsx: ts.JsxEmit.React
60
- });
126
+ function isRelativeSpecifier(specifier) {
127
+ return specifier.startsWith(".") || specifier.startsWith("/");
61
128
  }
62
- function transform(sourceFile, transformers) {
63
- const result = ts.transform(sourceFile, transformers);
64
- const transformedFile = result.transformed[0];
65
- result.dispose();
66
- return transformedFile;
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;
67
172
  }
68
173
 
69
174
  //#endregion
70
175
  //#region src/react.ts
71
- /**
72
- * Check if code uses JSX but missing React import
73
- */
176
+ /** Check if code uses JSX without React import */
74
177
  function needsReactImport(sourceFile) {
75
178
  let hasJSX = false;
76
179
  let hasReactVariable = false;
@@ -78,8 +181,7 @@ function needsReactImport(sourceFile) {
78
181
  if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) hasJSX = true;
79
182
  if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
80
183
  if (node.importClause) {
81
- var _node$importClause$na;
82
- if (((_node$importClause$na = node.importClause.name) === null || _node$importClause$na === void 0 ? void 0 : _node$importClause$na.text) === "React") hasReactVariable = true;
184
+ if (node.importClause.name?.text === "React") hasReactVariable = true;
83
185
  if (node.importClause.namedBindings && ts.isNamespaceImport(node.importClause.namedBindings)) {
84
186
  if (node.importClause.namedBindings.name.text === "React") hasReactVariable = true;
85
187
  }
@@ -93,9 +195,7 @@ function needsReactImport(sourceFile) {
93
195
  visitNode(sourceFile);
94
196
  return hasJSX && !hasReactVariable;
95
197
  }
96
- /**
97
- * Add React import statement at the top if missing
98
- */
198
+ /** Add React import statement to the top */
99
199
  function addReactImport(sourceFile) {
100
200
  let reactSpecifier = "react";
101
201
  function findReactSpecifier(node) {
@@ -114,169 +214,234 @@ function addReactImport(sourceFile) {
114
214
  }
115
215
 
116
216
  //#endregion
117
- //#region src/op.ts
118
- /**
119
- * track blob URLs to their original source URLs
120
- */
121
- const blobMap = /* @__PURE__ */ new Map();
122
- /**
123
- * track original source URLs to their transformed blob URLs
124
- */
125
- const sourceMap = /* @__PURE__ */ new Map();
126
- function track(sourceUrl, blobUrl) {
127
- sourceMap.set(sourceUrl, blobUrl);
128
- blobMap.set(blobUrl, sourceUrl);
129
- }
130
- /**
131
- * Given a source URL and source code, transform the module and return a blob URL,
132
- * where the blob URL's content is the transformed module code.
133
- */
134
- function transformSourceModule(_x, _x2) {
135
- return _transformSourceModule.apply(this, arguments);
136
- }
137
- function _transformSourceModule() {
138
- _transformSourceModule = _asyncToGenerator(function* (sourceUrl, sourceCode) {
139
- if (sourceMap.has(sourceUrl)) return sourceMap.get(sourceUrl);
140
- const code = `import.meta.url=${JSON.stringify(sourceUrl)};\n` + (yield rewriteModuleImport(sourceUrl, sourceCode));
141
- const blob = new Blob([code], { type: "text/javascript" });
142
- const blobUrl = URL.createObjectURL(blob);
143
- track(sourceUrl, blobUrl);
144
- return blobUrl;
145
- });
146
- return _transformSourceModule.apply(this, arguments);
147
- }
148
- function getFileName(sourceUrl) {
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) {
149
255
  try {
150
- return new URL(sourceUrl).pathname;
151
- } catch (_unused) {
152
- return "temp.tsx";
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 });
153
259
  }
154
260
  }
155
- function rewriteModuleImport(_x3, _x4) {
156
- return _rewriteModuleImport.apply(this, arguments);
157
- }
158
- function _rewriteModuleImport() {
159
- _rewriteModuleImport = _asyncToGenerator(function* (sourceUrl, sourceCode) {
160
- const sourceFile = createSourceFile(sourceCode, getFileName(sourceUrl));
161
- const rewrittenSpecifiers = yield resolveRelativeSpecifiers(collectRelativeSpecifiers(sourceFile), sourceUrl);
162
- let workingSourceFile = sourceFile;
163
- if (needsReactImport(workingSourceFile)) workingSourceFile = addReactImport(workingSourceFile);
164
- const transformers = [createRewriteImportTransformer(rewrittenSpecifiers)];
165
- return printSourceFile(transform(workingSourceFile, transformers));
166
- });
167
- return _rewriteModuleImport.apply(this, arguments);
168
- }
169
- function isBareSpecifier(specifier) {
170
- if (specifier.match(/^\.*\//)) return false;
261
+ function printSourceFile(sourceFile) {
171
262
  try {
172
- new URL(specifier);
173
- return false;
174
- } catch (_unused2) {
175
- return true;
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 });
176
276
  }
177
277
  }
178
- function isRelativeSpecifier(specifier) {
179
- return specifier.startsWith(".") || specifier.startsWith("/");
180
- }
181
- function collectRelativeSpecifiers(sourceFile) {
182
- const set = /* @__PURE__ */ new Set();
183
- const visit = (node) => {
184
- const addIfRelative = (literal) => {
185
- if (literal && isRelativeSpecifier(literal.text)) set.add(literal.text);
186
- };
187
- if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) addIfRelative(node.moduleSpecifier);
188
- if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
189
- const arg = node.arguments[0];
190
- if (arg && ts.isStringLiteral(arg)) addIfRelative(arg);
191
- }
192
- if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) addIfRelative(node.moduleSpecifier);
193
- ts.forEachChild(node, visit);
194
- };
195
- visit(sourceFile);
196
- return set;
197
- }
198
- function resolveRelativeSpecifiers(_x6, _x7) {
199
- return _resolveRelativeSpecifiers.apply(this, arguments);
200
- }
201
- function _resolveRelativeSpecifiers() {
202
- _resolveRelativeSpecifiers = _asyncToGenerator(function* (specifiers, sourceUrl) {
203
- const resolved = /* @__PURE__ */ new Map();
204
- const tasks = Array.from(specifiers).map(function() {
205
- var _ref = _asyncToGenerator(function* (specifier) {
206
- const targetUrl = new URL(specifier, sourceUrl).toString();
207
- const blobUrl = yield transformSourceModule(targetUrl, yield fetchModule(targetUrl));
208
- resolved.set(specifier, blobUrl);
209
- });
210
- return function(_x5) {
211
- return _ref.apply(this, arguments);
212
- };
213
- }());
214
- yield Promise.all(tasks);
215
- return resolved;
216
- });
217
- return _resolveRelativeSpecifiers.apply(this, arguments);
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
+ }
218
287
  }
219
- function createRewriteImportTransformer(replacements) {
220
- const rewriteSpecifier = (specifier) => {
221
- if (replacements.has(specifier)) return replacements.get(specifier);
222
- if (specifier.startsWith("npm:")) return specifier.replace(/^npm:/, "https://esm.sh/");
223
- if (specifier.startsWith("node:")) return `https://raw.esm.sh/@jspm/core/nodelibs/browser/${specifier.slice(5)}.js`;
224
- if (isBareSpecifier(specifier)) return new URL(specifier, "https://esm.sh/").toString();
225
- if (isRelativeSpecifier(specifier)) throw new Error("Unreachable");
226
- return specifier;
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());
227
299
  };
228
- const transformer = (context) => {
229
- const visitNode = (node) => {
230
- if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
231
- const next = rewriteSpecifier(node.moduleSpecifier.text);
232
- if (next !== node.moduleSpecifier.text) return ts.factory.updateImportDeclaration(node, node.modifiers, node.importClause, ts.factory.createStringLiteral(next), node.assertClause);
233
- }
234
- if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword) {
235
- const arg = node.arguments[0];
236
- if (arg && ts.isStringLiteral(arg)) {
237
- const next = rewriteSpecifier(arg.text);
238
- if (next !== arg.text) return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ts.factory.createStringLiteral(next), ...node.arguments.slice(1)]);
239
- }
240
- }
241
- if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
242
- const next = rewriteSpecifier(node.moduleSpecifier.text);
243
- if (next !== node.moduleSpecifier.text) return ts.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, ts.factory.createStringLiteral(next), node.assertClause);
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);
244
321
  }
245
- return ts.visitEachChild(node, visitNode, context);
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));
246
380
  };
247
- return (sf) => ts.visitNode(sf, visitNode);
248
- };
249
- return transformer;
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
+ }
250
418
  }
251
419
 
252
420
  //#endregion
253
421
  //#region src/index.ts
422
+ /**
423
+ * The singleton global instance of ModuleTSX.
424
+ */
425
+ const instance = new ModuleTSX();
254
426
  const TYPE_ATTRIBUTE_VALUE = "module-tsx";
255
- function runScript(_x) {
256
- return _runScript.apply(this, arguments);
257
- }
258
- function _runScript() {
259
- _runScript = _asyncToGenerator(function* (script) {
260
- return script.src ? import(yield transformSourceModule(script.src, yield fetchModule(script.src, { priority: script.fetchPriority }))) : import(yield transformSourceModule(location.href, script.innerHTML));
261
- });
262
- return _runScript.apply(this, arguments);
263
- }
264
- function sideEffect() {
265
- return _sideEffect.apply(this, arguments);
266
- }
267
- function _sideEffect() {
268
- _sideEffect = _asyncToGenerator(function* () {
269
- for (const s of Array.from(document.querySelectorAll(`script[type="${TYPE_ATTRIBUTE_VALUE}"]`))) {
270
- const script = s;
271
- if (!script.async && script.defer) console.warn(`script with type="${TYPE_ATTRIBUTE_VALUE}" does not support defer attribute. Use async or no attribute instead.`);
272
- for (const key in ["integrity", "crossorigin"]) if (script[key]) console.warn(`script with type="${TYPE_ATTRIBUTE_VALUE}" does not support ${key} attribute.`);
273
- if (script.async) runScript(script);
274
- else yield runScript(script);
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);
275
434
  }
276
- });
277
- return _sideEffect.apply(this, arguments);
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
+ }
278
443
  }
279
444
  document.addEventListener("DOMContentLoaded", sideEffect);
280
445
 
281
446
  //#endregion
282
- export { transformSourceModule };
447
+ export { ModuleTSX, ModuleTSXError, instance };