-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript-ts.ts
More file actions
108 lines (90 loc) · 2.91 KB
/
script-ts.ts
File metadata and controls
108 lines (90 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import tsBlankSpace from "ts-blank-space";
function setTimeoutAsync(ms?: Parameters<typeof setTimeout>[1]) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export class ScriptTs extends HTMLElement {
static observedAttributes: string[] = ["src"];
public originalSource: string;
public actualScript: HTMLScriptElement;
constructor() {
super();
}
log(
level: Extract<
keyof typeof console,
"log" | "debug" | "error" | "trace" | "warn" | "info"
>,
...args: unknown[]
) {
// silence console messages unless explicitly asked for
if (!location.search?.includes("script-ts-log=enable")) {
return;
}
const maybeId = this.attributes.getNamedItem("id")?.value;
const maybeSrc = this.attributes.getNamedItem("src")?.value;
const logDecorator =
maybeId != null ? `#${maybeId}` : maybeSrc != null ? `@${maybeSrc}` : "";
console[level](`[ScriptTs${logDecorator}]`, ...args);
}
removeImports = ScriptTs.removeImports;
removeTypes = ScriptTs.removeTypes;
hasSrc() {
return this.attributes.getNamedItem("src") != null;
}
connectedCallback() {
this.log("debug", "connectedCallback");
// at this point we know the src attribute but the attributeChangedCallback will also fire
this.hasSrc() ||
this.extractSource().then((source) => {
this.updateScript(source);
});
}
attributeChangedCallback(name: string, oldValue: unknown, newValue: unknown) {
this.log("debug", `attribute = ${name}`, `${oldValue} -> ${newValue}`);
switch (name) {
case "src":
this.extractSource().then((source) => {
this.updateScript(source);
});
break;
default:
this.log("warn", `change to attribute "${name}" not handled`)
}
}
async extractSource() {
const maybeSrc = this.attributes.getNamedItem("src");
if (maybeSrc != null) {
this.originalSource = await (await fetch(`${maybeSrc.value}`)).text();
} else {
await setTimeoutAsync();
this.originalSource = this.firstChild?.textContent ?? "";
}
return this.originalSource;
}
static removeImports(source: string) {
return source
.replaceAll("import", "// import")
.replaceAll("export", "/* export */");
}
static removeTypes(source: string) {
return tsBlankSpace(source);
}
updateScript(source: string) {
try {
if (this.actualScript != null) {
this.removeChild(this.actualScript);
}
this.textContent = "";
source = ScriptTs.removeImports(source);
source = ScriptTs.removeTypes(source);
this.log("debug", "updateScript ", `source = ${source}`);
this.actualScript = document.createElement("script");
this.actualScript.text = source;
this.appendChild(this.actualScript);
} catch (e) {
this.log("error", "updateScript", e);
throw e;
}
}
}
customElements.define("script-ts", ScriptTs);