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 +39 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +447 -0
- package/dist/index.mjs +369 -204
- package/dist/index.umd.js +118 -104
- package/package.json +19 -5
- package/dist/index.d.mts +0 -8
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
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
11
|
+
};
|
|
12
|
+
/** Log warnings with consistent formatting */
|
|
13
|
+
function warn(message, ...args) {
|
|
14
|
+
console.warn(`[module-tsx] ${message}`, ...args);
|
|
12
15
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
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/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
50
|
-
|
|
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
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
151
|
-
} catch (
|
|
152
|
-
|
|
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
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
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 {
|
|
447
|
+
export { ModuleTSX, ModuleTSXError, instance };
|