-
Notifications
You must be signed in to change notification settings - Fork 0
/
AstroScript.astro
119 lines (107 loc) · 2.99 KB
/
AstroScript.astro
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
109
110
111
112
113
114
115
116
117
118
119
---
import { minify as terse } from "terser";
import fs from "node:fs";
import { writeFile } from "node:fs/promises";
import path from "node:path";
import crypto from "node:crypto";
import { createRequire } from "node:module";
const moduleRequire = createRequire(import.meta.url);
const { JSDOM } = moduleRequire("jsdom");
export interface Props {
src?: string | string[];
inline?: boolean;
minify?: boolean;
minifyOptions?: object;
publicPath?: string;
dev?: boolean;
}
const {
src,
inline = true,
minify = true,
minifyOptions = {},
publicPath = "public",
dev = import.meta.env.NODE_ENV !== "development",
} = Astro.props as Props;
const slot = Astro.slots.default;
const sources = Array.isArray(src) ? src : [src];
if (!(src || slot)) {
throw new Error(
"The src Prop can't be empty when no child script is passed!"
);
}
const script = await (async () => {
if (!slot) {
sources.forEach((src) => {
if (!src.startsWith("/")) {
throw new Error(
"The src Prop must start with '/' and your JS files must be inside the public folder or resolved with Astro.resolve!"
);
}
if (path.extname(src) !== ".js") {
throw new Error("The src Prop must be a JS file!");
}
});
if (dev) {
let script = "";
script = (
await Promise.all(
sources.map(async (src) => {
if (src.startsWith("http://") || src.startsWith("https://")) {
return await (await fetch(src)).text();
} else {
const filepath = src.startsWith("/_astro/")
? src.replace("/_astro/", "")
: "public" + src;
return fs.readFileSync(filepath, "utf8");
}
})
)
).join("\n");
script = minify ? (await terse(script, minifyOptions)).code : script;
if (inline) {
return `<script>${script}</script>`;
} else {
const dir = `${publicPath}/astro-script`;
const hash = crypto
.createHash("sha256")
.update(script)
.digest("hex")
.substr(0, 8);
fs.existsSync(dir) || fs.mkdirSync(dir);
return writeFile(`${dir}/${hash}.js`, script).then(
() => `<script src="/astro-script/${hash}.js"></script>`
);
}
}
}
})();
if (src && slot) {
console.warn(
"WARNING: Inlined scripts are ignored when the src Prop is present"
);
}
---
<!-- prettier-ignore -->
{
src
? dev
? script
: sources.map((src) => <script src={src} />)
: slot
? (async () => {
let script = "";
if (dev) {
const { document } = new JSDOM(await (<slot />)).window;
const { scripts } = document;
script = [...scripts].map((script) => script.textContent).join("\n");
script = `<script>${
minify ? (await terse(script)).code : script
}</script>`;
} else {
script = await (<slot />);
}
return script;
})()
: ""
}