hikkaku 0.2.0 → 0.3.0
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 +31 -4
- package/assets/index.d.mts +1143 -783
- package/assets/index.mjs +1612 -2
- package/blocks/index.d.mts +169 -4
- package/blocks/index.mjs +197 -4
- package/chunk-DQk6qfdC.mjs +18 -0
- package/client/index.mjs +8 -1
- package/index.d.mts +2 -2
- package/index.mjs +31 -5
- package/package.json +5 -1
- package/{project-Ca9rsVT3.d.mts → project-djJPtrq7.d.mts} +21 -5
- package/types.d.mts +21 -0
- package/vite/index.mjs +112 -5
- /package/{composer-BudVTTBs.mjs → composer-DpyUR2R3.mjs} +0 -0
package/vite/index.mjs
CHANGED
|
@@ -1,15 +1,66 @@
|
|
|
1
1
|
import { zip } from "fflate";
|
|
2
|
-
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
import { createServerModuleRunner } from "vite";
|
|
6
|
+
import crypto from "node:crypto";
|
|
6
7
|
|
|
8
|
+
//#region src/vite/plugin-scratch-import.ts
|
|
9
|
+
const pluginScratchImport = () => ({
|
|
10
|
+
name: "vite-plugin-hikkaku:scratch-import",
|
|
11
|
+
enforce: "pre",
|
|
12
|
+
resolveId(source, importer, _options) {
|
|
13
|
+
if (source.endsWith("?scratch")) {
|
|
14
|
+
if (source.endsWith(".svg?scratch") || source.endsWith(".png?scratch") || source.endsWith(".wav?scratch") || source.endsWith(".mp3?scratch")) {
|
|
15
|
+
const importerPath = pathToFileURL(importer ?? "");
|
|
16
|
+
return { id: `\0scratch:${new URL(source, importerPath)}` };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async load(id, _options) {
|
|
21
|
+
if (id.startsWith("\0scratch:")) {
|
|
22
|
+
const url = new URL(id.slice(9, -8));
|
|
23
|
+
const ext = url.pathname.split(".").pop();
|
|
24
|
+
if (!ext) throw new Error(`Unsupported scratch asset type: ${url.pathname}`);
|
|
25
|
+
const file = await readFile(fileURLToPath(url));
|
|
26
|
+
const hash = crypto.createHash("md5");
|
|
27
|
+
hash.update(file);
|
|
28
|
+
const md5 = hash.digest("hex");
|
|
29
|
+
const data = {
|
|
30
|
+
name: path.basename(url.pathname),
|
|
31
|
+
_data: Buffer.from(file).toString("base64"),
|
|
32
|
+
assetId: md5,
|
|
33
|
+
dataFormat: ext,
|
|
34
|
+
md5ext: `${md5}.${ext}`
|
|
35
|
+
};
|
|
36
|
+
return `
|
|
37
|
+
const data = ${JSON.stringify(data)}
|
|
38
|
+
// to Uint8Array
|
|
39
|
+
data._data = Uint8Array.from(atob(data._data), c => c.charCodeAt(0));
|
|
40
|
+
|
|
41
|
+
export default data
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
7
48
|
//#region src/vite/index.ts
|
|
8
49
|
const BASE_URL = "https://scratchfoundation.github.io/scratch-gui/";
|
|
9
50
|
const VIRTUAL_MODULE_IDS = { project: "/@virtual/hikkaku-project" };
|
|
10
51
|
function hikkaku(init) {
|
|
11
52
|
let runner = null;
|
|
12
|
-
|
|
53
|
+
let additionalAssets = /* @__PURE__ */ new Map();
|
|
54
|
+
const assetCache = /* @__PURE__ */ new Map();
|
|
55
|
+
const setContentType = (res, assetId) => {
|
|
56
|
+
const assetExt = path.extname(assetId).toLowerCase();
|
|
57
|
+
if (assetExt === ".png") res.setHeader("Content-Type", "image/png");
|
|
58
|
+
else if (assetExt === ".jpg" || assetExt === ".jpeg") res.setHeader("Content-Type", "image/jpeg");
|
|
59
|
+
else if (assetExt === ".wav") res.setHeader("Content-Type", "audio/wav");
|
|
60
|
+
else if (assetExt === ".mp3") res.setHeader("Content-Type", "audio/mpeg");
|
|
61
|
+
else res.setHeader("Content-Type", "application/octet-stream");
|
|
62
|
+
};
|
|
63
|
+
return [{
|
|
13
64
|
name: "vite-plugin-hikkaku",
|
|
14
65
|
config(config, env) {
|
|
15
66
|
if (env.command === "build") (config.plugins?.find((p) => p && typeof p === "object" && "name" in p && p.name === "vite-plugin-turbowarp-packager"))?.api.setEntry(path.join(process.cwd(), "dist", "project.sb3"));
|
|
@@ -38,8 +89,12 @@ function hikkaku(init) {
|
|
|
38
89
|
}
|
|
39
90
|
const { default: project } = await import(pathToFileURL(path.join(process.cwd(), "dist/.tmp", "project.mjs")).href);
|
|
40
91
|
const projectJSON = project.toScratch();
|
|
92
|
+
const assets = project.getAdditionalAssets();
|
|
41
93
|
const zipData = await new Promise((resolve, reject) => {
|
|
42
|
-
zip({
|
|
94
|
+
zip({
|
|
95
|
+
"project.json": new TextEncoder().encode(JSON.stringify(projectJSON)),
|
|
96
|
+
...Object.fromEntries(assets.entries())
|
|
97
|
+
}, (err, data) => {
|
|
43
98
|
if (err) reject(err);
|
|
44
99
|
else resolve(data);
|
|
45
100
|
});
|
|
@@ -56,6 +111,12 @@ function hikkaku(init) {
|
|
|
56
111
|
name: "project.json",
|
|
57
112
|
source: JSON.stringify(projectJSON, null, 2)
|
|
58
113
|
});
|
|
114
|
+
for (const [assetId, data] of assets.entries()) this.emitFile({
|
|
115
|
+
type: "asset",
|
|
116
|
+
fileName: `assets/${assetId}`,
|
|
117
|
+
name: `assets/${assetId}`,
|
|
118
|
+
source: data
|
|
119
|
+
});
|
|
59
120
|
await rm(tmpDir, {
|
|
60
121
|
recursive: true,
|
|
61
122
|
force: true
|
|
@@ -88,6 +149,7 @@ function hikkaku(init) {
|
|
|
88
149
|
if (this.environment.name !== "hikkaku") return;
|
|
89
150
|
if (!runner) throw new Error("Module runner is not initialized.");
|
|
90
151
|
const project = (await runner.import(init.entry)).default;
|
|
152
|
+
additionalAssets = project.getAdditionalAssets();
|
|
91
153
|
options.server.environments.client.hot.send("hikkaku:project", project.toScratch());
|
|
92
154
|
},
|
|
93
155
|
async configureServer(server) {
|
|
@@ -98,9 +160,54 @@ function hikkaku(init) {
|
|
|
98
160
|
server.environments.client.hot.on("vite:client:connect", async () => {
|
|
99
161
|
if (!runner) throw new Error("Module runner is not initialized.");
|
|
100
162
|
const project = (await runner.import(init.entry)).default;
|
|
163
|
+
additionalAssets = project.getAdditionalAssets();
|
|
101
164
|
server.environments.client.hot.send("hikkaku:project", project.toScratch());
|
|
102
165
|
});
|
|
103
166
|
server.middlewares.use(async (req, res, next) => {
|
|
167
|
+
if (req.url?.startsWith("/hikkaku-assets/")) {
|
|
168
|
+
const segments = req.url.split("/");
|
|
169
|
+
const assetId = segments[segments.indexOf("hikkaku-assets") + 1];
|
|
170
|
+
if (!assetId) {
|
|
171
|
+
res.statusCode = 400;
|
|
172
|
+
res.end("Asset ID is required");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const assetData = additionalAssets.get(assetId);
|
|
176
|
+
if (!assetData) {
|
|
177
|
+
if (assetCache.has(assetId)) {
|
|
178
|
+
const cached = assetCache.get(assetId);
|
|
179
|
+
if (cached === false) {
|
|
180
|
+
res.statusCode = 404;
|
|
181
|
+
res.end("Asset not found");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
setContentType(res, assetId);
|
|
185
|
+
res.end(cached);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const assetData = await fetch(`https://assets.scratch.mit.edu/internalapi/asset/${assetId}/get/`).then((r) => {
|
|
189
|
+
if (!r.ok) return null;
|
|
190
|
+
return r.arrayBuffer();
|
|
191
|
+
}).catch((e) => {
|
|
192
|
+
console.warn("Failed to fetch asset from network:", e);
|
|
193
|
+
return null;
|
|
194
|
+
});
|
|
195
|
+
if (assetData) {
|
|
196
|
+
const uint8array = new Uint8Array(assetData);
|
|
197
|
+
assetCache.set(assetId, uint8array);
|
|
198
|
+
setContentType(res, assetId);
|
|
199
|
+
res.end(uint8array);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
assetCache.set(assetId, false);
|
|
203
|
+
res.statusCode = 404;
|
|
204
|
+
res.end("Asset not found");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
setContentType(res, assetId);
|
|
208
|
+
res.end(assetData);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
104
211
|
if (req.url === "/") {
|
|
105
212
|
const html = (await fetch(BASE_URL).then((res) => res.text())).replace("gui.js", "https://scratchfoundation.github.io/scratch-gui/gui.js").replace("</head>", "<script src=\"/@vite/client\" type=\"module\"><\/script><script type=\"module\" src=\"/@virtual/hikkaku-client\"><\/script></head>");
|
|
106
213
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -122,7 +229,7 @@ function hikkaku(init) {
|
|
|
122
229
|
next();
|
|
123
230
|
});
|
|
124
231
|
}
|
|
125
|
-
};
|
|
232
|
+
}, pluginScratchImport()];
|
|
126
233
|
}
|
|
127
234
|
|
|
128
235
|
//#endregion
|
|
File without changes
|