hikkaku 0.2.1 → 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.
@@ -1,4 +1,4 @@
1
- import { S as PrimitiveSource, _ as HikkakuBlock, k as VariableReference, m as CostumeSource, w as SoundSource, y as ListReference } from "../project-Ca9rsVT3.mjs";
1
+ import { D as SoundSource, M as VariableReference, g as CostumeSource, w as PrimitiveSource, x as ListReference, y as HikkakuBlock } from "../project-djJPtrq7.mjs";
2
2
 
3
3
  //#region src/blocks/control.d.ts
4
4
  type StopOption = 'all' | 'this script' | 'other scripts in sprite' | 'other scripts in stage';
@@ -2570,9 +2570,18 @@ type SoundEffect = 'pitch' | 'pan';
2570
2570
  * @returns Scratch statement block definition that is appended to the current script stack.
2571
2571
  * @example
2572
2572
  * ```ts
2573
+ * import { Project } from 'hikkaku'
2574
+ * import { SOUNDS } from 'hikkaku/assets'
2573
2575
  * import { playSound } from 'hikkaku/blocks'
2574
2576
  *
2575
- * playSound('pop')
2577
+ * const project = new Project()
2578
+ * const sprite = project.createSprite('Sprite')
2579
+ * const sound = sprite.addSound({
2580
+ * ...SOUNDS.COMPUTER_BEEP,
2581
+ * name: 'beep',
2582
+ * })
2583
+ *
2584
+ * playSound(sound)
2576
2585
  * ```
2577
2586
  */
2578
2587
  declare const playSound: (sound: SoundSource) => HikkakuBlock;
@@ -2586,9 +2595,17 @@ declare const playSound: (sound: SoundSource) => HikkakuBlock;
2586
2595
  * @returns Scratch statement block definition that is appended to the current script stack.
2587
2596
  * @example
2588
2597
  * ```ts
2598
+ * import { Project } from 'hikkaku'
2599
+ * import { SOUNDS } from 'hikkaku/assets'
2589
2600
  * import { playSoundUntilDone } from 'hikkaku/blocks'
2590
2601
  *
2591
- * playSoundUntilDone('pop')
2602
+ * const project = new Project()
2603
+ * const sound = project.addSound({
2604
+ * ...SOUNDS.COMPUTER_BEEP,
2605
+ * name: 'beep',
2606
+ * })
2607
+ *
2608
+ * playSoundUntilDone(sound)
2592
2609
  * ```
2593
2610
  */
2594
2611
  declare const playSoundUntilDone: (sound: SoundSource) => HikkakuBlock;
package/blocks/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as valueBlock, c as fromSoundSource, i as substack, l as InputType, n as block, o as fromCostumeSource, s as fromPrimitiveSource, t as attachStack, u as Shadow } from "../composer-BudVTTBs.mjs";
1
+ import { a as valueBlock, c as fromSoundSource, i as substack, l as InputType, n as block, o as fromCostumeSource, s as fromPrimitiveSource, t as attachStack, u as Shadow } from "../composer-DpyUR2R3.mjs";
2
2
 
3
3
  //#region src/blocks/control.ts
4
4
  /**
@@ -3136,9 +3136,18 @@ const getUsername = () => {
3136
3136
  * @returns Scratch statement block definition that is appended to the current script stack.
3137
3137
  * @example
3138
3138
  * ```ts
3139
+ * import { Project } from 'hikkaku'
3140
+ * import { SOUNDS } from 'hikkaku/assets'
3139
3141
  * import { playSound } from 'hikkaku/blocks'
3140
3142
  *
3141
- * playSound('pop')
3143
+ * const project = new Project()
3144
+ * const sprite = project.createSprite('Sprite')
3145
+ * const sound = sprite.addSound({
3146
+ * ...SOUNDS.COMPUTER_BEEP,
3147
+ * name: 'beep',
3148
+ * })
3149
+ *
3150
+ * playSound(sound)
3142
3151
  * ```
3143
3152
  */
3144
3153
  const playSound = (sound) => {
@@ -3154,9 +3163,17 @@ const playSound = (sound) => {
3154
3163
  * @returns Scratch statement block definition that is appended to the current script stack.
3155
3164
  * @example
3156
3165
  * ```ts
3166
+ * import { Project } from 'hikkaku'
3167
+ * import { SOUNDS } from 'hikkaku/assets'
3157
3168
  * import { playSoundUntilDone } from 'hikkaku/blocks'
3158
3169
  *
3159
- * playSoundUntilDone('pop')
3170
+ * const project = new Project()
3171
+ * const sound = project.addSound({
3172
+ * ...SOUNDS.COMPUTER_BEEP,
3173
+ * name: 'beep',
3174
+ * })
3175
+ *
3176
+ * playSoundUntilDone(sound)
3160
3177
  * ```
3161
3178
  */
3162
3179
  const playSoundUntilDone = (sound) => {
@@ -0,0 +1,18 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) {
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ }
11
+ if (!no_symbols) {
12
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ }
14
+ return target;
15
+ };
16
+
17
+ //#endregion
18
+ export { __exportAll as t };
package/client/index.mjs CHANGED
@@ -35,7 +35,14 @@ const getScratchInternalStates = (root) => {
35
35
  }
36
36
  return false;
37
37
  });
38
- vm.runtime.storage.webHelper.assetTool.tools.splice(0, 1);
38
+ const storage = vm.runtime.storage;
39
+ storage.webHelper.assetTool.tools.splice(0, 1);
40
+ storage.webHelper.stores.splice(0, storage.webHelper.stores.length);
41
+ storage.addWebStore([
42
+ storage.AssetType.ImageVector,
43
+ storage.AssetType.ImageBitmap,
44
+ storage.AssetType.Sound
45
+ ], (asset) => `/hikkaku-assets/${asset.assetId}.${asset.dataFormat}`, null, null);
39
46
  return {
40
47
  reduxState,
41
48
  vm,
package/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { C as SoundReference, D as VariableMonitorMode, E as VariableDefinition, O as VariableMonitorOptions, S as PrimitiveSource, T as VariableBase, _ as HikkakuBlock, a as attachStack, b as MonitorPosition, c as substack, d as fromPrimitiveSource, f as fromSoundSource, g as CreateVariableOptions, h as CreateListOptions, i as Handler, k as VariableReference, l as valueBlock, m as CostumeSource, n as Target, o as block, p as CostumeReference, r as BlockInit, s as createBlocks, t as Project, u as fromCostumeSource, v as ListMonitorOptions, w as SoundSource, x as PrimitiveAvailableOnScratch, y as ListReference } from "./project-Ca9rsVT3.mjs";
2
- export { BlockInit, CostumeReference, CostumeSource, CreateListOptions, CreateVariableOptions, Handler, HikkakuBlock, ListMonitorOptions, ListReference, MonitorPosition, PrimitiveAvailableOnScratch, PrimitiveSource, Project, SoundReference, SoundSource, Target, VariableBase, VariableDefinition, VariableMonitorMode, VariableMonitorOptions, VariableReference, attachStack, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
1
+ import { A as VariableMonitorMode, C as PrimitiveAvailableOnScratch, D as SoundSource, E as SoundReference, M as VariableReference, O as VariableBase, S as MonitorPosition, T as SoundData, _ as CreateListOptions, a as Handler, b as ListMonitorOptions, c as createBlocks, d as fromCostumeSource, f as fromPrimitiveSource, g as CostumeSource, h as CostumeReference, i as BlockInit, j as VariableMonitorOptions, k as VariableDefinition, l as substack, m as CostumeData, n as SpriteOptions, o as attachStack, p as fromSoundSource, r as Target, s as block, t as Project, u as valueBlock, v as CreateVariableOptions, w as PrimitiveSource, x as ListReference, y as HikkakuBlock } from "./project-djJPtrq7.mjs";
2
+ export { BlockInit, CostumeData, CostumeReference, CostumeSource, CreateListOptions, CreateVariableOptions, Handler, HikkakuBlock, ListMonitorOptions, ListReference, MonitorPosition, PrimitiveAvailableOnScratch, PrimitiveSource, Project, SoundData, SoundReference, SoundSource, SpriteOptions, Target, VariableBase, VariableDefinition, VariableMonitorMode, VariableMonitorOptions, VariableReference, attachStack, block, createBlocks, fromCostumeSource, fromPrimitiveSource, fromSoundSource, substack, valueBlock };
package/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as valueBlock, c as fromSoundSource, i as substack, n as block, o as fromCostumeSource, r as createBlocks, s as fromPrimitiveSource, t as attachStack } from "./composer-BudVTTBs.mjs";
1
+ import { a as valueBlock, c as fromSoundSource, i as substack, n as block, o as fromCostumeSource, r as createBlocks, s as fromPrimitiveSource, t as attachStack } from "./composer-DpyUR2R3.mjs";
2
2
 
3
3
  //#region src/core/monitors.ts
4
4
  const createVariableMonitor = (id, name, defaultValue, spriteName, options) => {
@@ -86,9 +86,15 @@ var Target = class {
86
86
  #monitors = [];
87
87
  #costumes = [];
88
88
  #sounds = [];
89
- constructor(isStage, name) {
89
+ #size;
90
+ #x;
91
+ #y;
92
+ constructor(isStage, name, options) {
90
93
  this.isStage = isStage;
91
94
  this.name = name;
95
+ this.#size = options?.size;
96
+ this.#x = options?.x;
97
+ this.#y = options?.y;
92
98
  }
93
99
  run(handler) {
94
100
  const blocks = createBlocks(() => {
@@ -171,9 +177,18 @@ var Target = class {
171
177
  ...target,
172
178
  isStage: false,
173
179
  name: this.name,
174
- visible: true
180
+ visible: true,
181
+ size: this.#size,
182
+ x: this.#x,
183
+ y: this.#y
175
184
  };
176
185
  }
186
+ getAdditionalAssets() {
187
+ const assets = /* @__PURE__ */ new Map();
188
+ for (const costume of this.#costumes) if (costume._data) assets.set(`${costume.assetId}.${costume.dataFormat}`, costume._data);
189
+ for (const sound of this.#sounds) if (sound._data) assets.set(`${sound.assetId}.${sound.dataFormat}`, sound._data);
190
+ return assets;
191
+ }
177
192
  };
178
193
  var Project = class {
179
194
  stage;
@@ -183,11 +198,17 @@ var Project = class {
183
198
  this.stage = target;
184
199
  this.#targets.push(target);
185
200
  }
186
- createSprite(name) {
187
- const sprite = new Target(false, name);
201
+ createSprite(name, options) {
202
+ const sprite = new Target(false, name, options);
188
203
  this.#targets.push(sprite);
189
204
  return sprite;
190
205
  }
206
+ addCostume(costume) {
207
+ return this.stage.addCostume(costume);
208
+ }
209
+ addSound(sound) {
210
+ return this.stage.addSound(sound);
211
+ }
191
212
  toScratch() {
192
213
  const targets = this.#targets.map((target) => target.toScratch());
193
214
  return {
@@ -200,6 +221,11 @@ var Project = class {
200
221
  }
201
222
  };
202
223
  }
224
+ getAdditionalAssets() {
225
+ const assets = /* @__PURE__ */ new Map();
226
+ for (const target of this.#targets) for (const [key, value] of target.getAdditionalAssets()) assets.set(key, value);
227
+ return assets;
228
+ }
203
229
  };
204
230
 
205
231
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hikkaku",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "exports": {
@@ -23,6 +23,10 @@
23
23
  "./vite": {
24
24
  "import": "./vite/index.mjs",
25
25
  "types": "./vite/index.d.mts"
26
+ },
27
+ "./types": {
28
+ "import": "./types.d.mjs",
29
+ "types": "./types.d.mts"
26
30
  }
27
31
  },
28
32
  "scripts": {
@@ -1,4 +1,5 @@
1
1
  import * as sb3 from "sb3-types";
2
+ import { Costume, Sound } from "sb3-types";
2
3
 
3
4
  //#region src/core/types.d.ts
4
5
  type PrimitiveAvailableOnScratch = number | boolean | string;
@@ -55,6 +56,12 @@ interface HikkakuBlock {
55
56
  isBlock: true;
56
57
  id: string;
57
58
  }
59
+ type CostumeData = Costume & {
60
+ _data?: Uint8Array;
61
+ };
62
+ type SoundData = Sound & {
63
+ _data?: Uint8Array;
64
+ };
58
65
  //#endregion
59
66
  //#region src/core/block-helper.d.ts
60
67
  declare const fromPrimitiveSource: <T extends PrimitiveAvailableOnScratch>(source: PrimitiveSource<T>) => sb3.Input;
@@ -114,26 +121,35 @@ interface ListMonitor {
114
121
  type Monitor = VariableMonitor | ListMonitor;
115
122
  //#endregion
116
123
  //#region src/core/project.d.ts
124
+ interface SpriteOptions {
125
+ size?: number;
126
+ x?: number;
127
+ y?: number;
128
+ }
117
129
  declare class Target<IsStage extends boolean = boolean> {
118
130
  #private;
119
131
  readonly isStage: IsStage;
120
132
  readonly name: IsStage extends true ? 'Stage' : string;
121
133
  currentCostume: number;
122
- constructor(isStage: IsStage, name: IsStage extends true ? 'Stage' : string);
134
+ constructor(isStage: IsStage, name: IsStage extends true ? 'Stage' : string, options?: IsStage extends false ? SpriteOptions : undefined);
123
135
  run(handler: (target: Target<IsStage>) => void): void;
124
136
  createVariable(name: string, defaultValue?: sb3.ScalarVal, isCloudVariableOrOptions?: boolean | CreateVariableOptions): VariableDefinition;
125
137
  createList(name: string, defaultValue?: sb3.ScalarVal[], options?: CreateListOptions): ListReference;
126
- addCostume(costume: sb3.Costume): CostumeReference;
127
- addSound(sound: sb3.Sound): SoundReference;
138
+ addCostume(costume: CostumeData): CostumeReference;
139
+ addSound(sound: SoundData): SoundReference;
128
140
  get monitors(): readonly Monitor[];
129
141
  toScratch(): IsStage extends true ? sb3.Stage : sb3.Sprite;
142
+ getAdditionalAssets(): Map<string, Uint8Array>;
130
143
  }
131
144
  declare class Project {
132
145
  #private;
133
146
  readonly stage: Target<true>;
134
147
  constructor();
135
- createSprite(name: string): Target<false>;
148
+ createSprite(name: string, options?: SpriteOptions): Target<false>;
149
+ addCostume(costume: CostumeData): CostumeReference;
150
+ addSound(sound: SoundData): SoundReference;
136
151
  toScratch(): sb3.ScratchProject;
152
+ getAdditionalAssets(): Map<string, Uint8Array>;
137
153
  }
138
154
  //#endregion
139
- export { SoundReference as C, VariableMonitorMode as D, VariableDefinition as E, VariableMonitorOptions as O, PrimitiveSource as S, VariableBase as T, HikkakuBlock as _, attachStack as a, MonitorPosition as b, substack as c, fromPrimitiveSource as d, fromSoundSource as f, CreateVariableOptions as g, CreateListOptions as h, Handler as i, VariableReference as k, valueBlock as l, CostumeSource as m, Target as n, block as o, CostumeReference as p, BlockInit as r, createBlocks as s, Project as t, fromCostumeSource as u, ListMonitorOptions as v, SoundSource as w, PrimitiveAvailableOnScratch as x, ListReference as y };
155
+ export { VariableMonitorMode as A, PrimitiveAvailableOnScratch as C, SoundSource as D, SoundReference as E, VariableReference as M, VariableBase as O, MonitorPosition as S, SoundData as T, CreateListOptions as _, Handler as a, ListMonitorOptions as b, createBlocks as c, fromCostumeSource as d, fromPrimitiveSource as f, CostumeSource as g, CostumeReference as h, BlockInit as i, VariableMonitorOptions as j, VariableDefinition as k, substack as l, CostumeData as m, SpriteOptions as n, attachStack as o, fromSoundSource as p, Target as r, block as s, Project as t, valueBlock as u, CreateVariableOptions as v, PrimitiveSource as w, ListReference as x, HikkakuBlock as y };
package/types.d.mts ADDED
@@ -0,0 +1,21 @@
1
+ //#region src/types.d.ts
2
+ declare module '*.svg?scratch' {
3
+ import * as sb3 from 'sb3-types';
4
+ const content: sb3.Costume;
5
+ export default content;
6
+ }
7
+ declare module '*.png?scratch' {
8
+ import * as sb3 from 'sb3-types';
9
+ const content: sb3.Costume;
10
+ export default content;
11
+ }
12
+ declare module '*.wav?scratch' {
13
+ import * as sb3 from 'sb3-types';
14
+ const content: sb3.Sound;
15
+ export default content;
16
+ }
17
+ declare module '*.mp3?scratch' {
18
+ import * as sb3 from 'sb3-types';
19
+ const content: sb3.Sound;
20
+ export default content;
21
+ }
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
- return {
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({ "project.json": new TextEncoder().encode(JSON.stringify(projectJSON)) }, (err, data) => {
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