vite-plugin-turbowarp-packager 0.1.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 ADDED
@@ -0,0 +1,71 @@
1
+ # vite-plugin-turbowarp-packager
2
+
3
+ A Vite plugin that uses `@turbowarp/packager` to generate TurboWarp-ready HTML from a `.sb3` file.
4
+ During build, it outputs `dist/turbowarp-packager.html`.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ bun add -D vite-plugin-turbowarp-packager
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Use with `hikkaku/vite` (recommended)
15
+
16
+ The `hikkaku` plugin automatically sets the output path for `project.sb3`.
17
+
18
+ ```ts
19
+ import hikkaku from 'hikkaku/vite'
20
+ import { defineConfig } from 'vite-plus'
21
+ import { pluginTurbowarpPackager } from 'vite-plugin-turbowarp-packager'
22
+
23
+ export default defineConfig({
24
+ plugins: [
25
+ hikkaku({
26
+ entry: 'src.ts',
27
+ }),
28
+ pluginTurbowarpPackager(),
29
+ ],
30
+ })
31
+ ```
32
+
33
+ When using both plugins, place `hikkaku(...)` before `pluginTurbowarpPackager(...)`.
34
+
35
+ ### Set `sb3Entry` manually
36
+
37
+ If your `.sb3` is generated by another process, set `sb3Entry` directly.
38
+
39
+ ```ts
40
+ import { defineConfig } from 'vite-plus'
41
+ import { pluginTurbowarpPackager } from 'vite-plugin-turbowarp-packager'
42
+
43
+ export default defineConfig({
44
+ plugins: [
45
+ pluginTurbowarpPackager({
46
+ sb3Entry: 'dist/project.sb3',
47
+ packagerOptions: {
48
+ turbo: true,
49
+ },
50
+ }),
51
+ ],
52
+ })
53
+ ```
54
+
55
+ ## Options
56
+
57
+ ```ts
58
+ interface TurbowarpPackagerOptions {
59
+ sb3Entry?: string
60
+ packagerOptions?: Partial<PackagerOptions>
61
+ }
62
+ ```
63
+
64
+ - `sb3Entry`: Path to the `.sb3` file to package. Build fails if omitted.
65
+ - `packagerOptions`: Options passed to `@turbowarp/packager`.
66
+
67
+ ## Limitations
68
+
69
+ - `packagerOptions.target` currently supports only `html`.
70
+ - Output filename is fixed to `turbowarp-packager.html`.
71
+ - This plugin is intended for build-time packaging (not dev-server conversion).
@@ -0,0 +1,11 @@
1
+ import { PackagerOptions } from "@turbowarp/packager";
2
+ import { PluginOption } from "vite";
3
+
4
+ //#region src/index.d.ts
5
+ interface TurbowarpPackagerOptions {
6
+ sb3Entry?: string;
7
+ packagerOptions?: Partial<PackagerOptions>;
8
+ }
9
+ declare const pluginTurbowarpPackager: (options?: TurbowarpPackagerOptions) => PluginOption;
10
+ //#endregion
11
+ export { TurbowarpPackagerOptions, pluginTurbowarpPackager };
package/dist/index.mjs ADDED
@@ -0,0 +1,80 @@
1
+ import { readFile } from "node:fs/promises";
2
+
3
+ //#region src/packager.ts
4
+ async function loadPackager() {
5
+ const packagerModule = await import("@turbowarp/packager");
6
+ const Packager = packagerModule.Packager ?? packagerModule.packager?.Packager ?? packagerModule.default?.Packager ?? packagerModule.default?.packager?.Packager;
7
+ const loadProject = packagerModule.loadProject ?? packagerModule.packager?.loadProject ?? packagerModule.default?.loadProject ?? packagerModule.default?.packager?.loadProject;
8
+ if (!Packager || !loadProject) throw new Error(`Could not find Packager or loadProject in @turbowarp/packager module. Keys: ${Object.keys(packagerModule)}`);
9
+ return {
10
+ Packager,
11
+ loadProject
12
+ };
13
+ }
14
+
15
+ //#endregion
16
+ //#region src/index.ts
17
+ const VIRTUAL_MODULE_IDS = {
18
+ html: "virtual:turbowarp-packager.html",
19
+ js: "virtual:turbowarp-packager.js"
20
+ };
21
+ const pluginTurbowarpPackager = (options) => {
22
+ let packedData = null;
23
+ const decoder = new TextDecoder();
24
+ const pack = async () => {
25
+ if (packedData) return packedData;
26
+ const sb3Entry = options?.sb3Entry;
27
+ if (!sb3Entry) throw new Error("sb3Entry is not set in TurbowarpPackagerOptions.");
28
+ const { loadProject, Packager } = await loadPackager();
29
+ const project = await loadProject(await readFile(sb3Entry));
30
+ const packager = new Packager();
31
+ const resolvedPackagerOptions = Object.assign({}, packager.options, { target: "html" }, options?.packagerOptions ?? {});
32
+ packager.options = resolvedPackagerOptions;
33
+ packager.project = project;
34
+ if (resolvedPackagerOptions.target !== "html") throw new Error(`Currently ${resolvedPackagerOptions.target} target is not supported in Vite plugin, only 'html' target is supported.`);
35
+ const packageResult = await packager.package();
36
+ const html = typeof packageResult.data === "string" ? packageResult.data : decoder.decode(packageResult.data);
37
+ packedData = html;
38
+ return html;
39
+ };
40
+ return {
41
+ name: "vite-plugin-turbowarp-packager",
42
+ api: { setEntry(sb3Entry) {
43
+ options = {
44
+ ...options,
45
+ sb3Entry
46
+ };
47
+ } },
48
+ config(_config, _env) {
49
+ if (!options?.sb3Entry) throw new Error("sb3Entry is not set in TurbowarpPackagerOptions.");
50
+ return {
51
+ environments: { turbowarpPackager: {
52
+ build: { rolldownOptions: {
53
+ input: VIRTUAL_MODULE_IDS.html,
54
+ output: { entryFileNames: "turbowarp-packager.html" }
55
+ } },
56
+ consumer: "client"
57
+ } },
58
+ builder: {}
59
+ };
60
+ },
61
+ async buildApp(builder) {
62
+ const env = builder.environments.turbowarpPackager;
63
+ if (!env) throw new Error("Turbowarp Packager environment is not configured.");
64
+ await builder.build(env);
65
+ },
66
+ resolveId(source) {
67
+ if (source === VIRTUAL_MODULE_IDS.html) return source;
68
+ },
69
+ async load(id) {
70
+ if (id === VIRTUAL_MODULE_IDS.html) {
71
+ const packed = await pack();
72
+ if (!packed) throw new Error("Packing failed.");
73
+ return { code: packed };
74
+ }
75
+ }
76
+ };
77
+ };
78
+
79
+ //#endregion
80
+ export { pluginTurbowarpPackager };
package/example/src.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { Project } from 'hikkaku'
2
+ import { say, whenFlagClicked } from 'hikkaku/blocks'
3
+
4
+ const project = new Project()
5
+
6
+ project.stage.run(() => {
7
+ whenFlagClicked(() => {
8
+ say('Hello, Turbowarp Packager!')
9
+ })
10
+ })
11
+
12
+ export default project
@@ -0,0 +1,15 @@
1
+ import hikkaku from 'hikkaku/vite'
2
+ import { defineConfig } from 'vite-plus'
3
+ import { pluginTurbowarpPackager } from '../src'
4
+
5
+ export default defineConfig({
6
+ plugins: [
7
+ hikkaku({
8
+ entry: 'src.ts',
9
+ }),
10
+ pluginTurbowarpPackager(),
11
+ ],
12
+ build: {
13
+ minify: false,
14
+ },
15
+ })
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "vite-plugin-turbowarp-packager",
3
+ "type": "module",
4
+ "version": "0.1.0",
5
+ "devDependencies": {
6
+ "@types/bun": "latest",
7
+ "vite-plus": "^0.0.0-3262bda4.20260210-0221"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.,ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "vp pack"
17
+ },
18
+ "peerDependencies": {
19
+ "typescript": "^5"
20
+ },
21
+ "dependencies": {
22
+ "@turbowarp/packager": "^3.11.0",
23
+ "fflate": "^0.8.2",
24
+ "hikkaku": "0.1.9",
25
+ "vite": "8.0.0-beta.13"
26
+ }
27
+ }
package/src/index.ts ADDED
@@ -0,0 +1,115 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import type { PackagerOptions } from '@turbowarp/packager'
3
+ import type { PluginOption } from 'vite'
4
+ import { loadPackager } from './packager'
5
+
6
+ const VIRTUAL_MODULE_IDS = {
7
+ html: 'virtual:turbowarp-packager.html',
8
+ js: 'virtual:turbowarp-packager.js',
9
+ } as const
10
+
11
+ export interface TurbowarpPackagerOptions {
12
+ sb3Entry?: string
13
+ packagerOptions?: Partial<PackagerOptions>
14
+ }
15
+
16
+ export const pluginTurbowarpPackager = (
17
+ options?: TurbowarpPackagerOptions,
18
+ ): PluginOption => {
19
+ let packedData: string | null = null
20
+ const decoder = new TextDecoder()
21
+ const pack = async () => {
22
+ if (packedData) {
23
+ return packedData
24
+ }
25
+ const sb3Entry = options?.sb3Entry
26
+ if (!sb3Entry) {
27
+ throw new Error('sb3Entry is not set in TurbowarpPackagerOptions.')
28
+ }
29
+ const { loadProject, Packager } = await loadPackager()
30
+ const sb3 = await readFile(sb3Entry)
31
+
32
+ const project = await loadProject(sb3)
33
+ const packager = new Packager()
34
+ const resolvedPackagerOptions = Object.assign(
35
+ {},
36
+ packager.options,
37
+ {
38
+ target: 'html',
39
+ },
40
+ options?.packagerOptions ?? {},
41
+ )
42
+ packager.options = resolvedPackagerOptions
43
+ packager.project = project
44
+ if (resolvedPackagerOptions.target !== 'html') {
45
+ throw new Error(
46
+ `Currently ${resolvedPackagerOptions.target} target is not supported in Vite plugin, only 'html' target is supported.`,
47
+ )
48
+ }
49
+ const packageResult = await packager.package()
50
+
51
+ const html =
52
+ typeof packageResult.data === 'string'
53
+ ? packageResult.data
54
+ : decoder.decode(packageResult.data)
55
+ packedData = html
56
+ return html
57
+ }
58
+ return {
59
+ name: 'vite-plugin-turbowarp-packager',
60
+ api: {
61
+ setEntry(sb3Entry: string) {
62
+ options = {
63
+ ...options,
64
+ sb3Entry,
65
+ }
66
+ },
67
+ },
68
+ config(_config, _env) {
69
+ const sb3Entry = options?.sb3Entry
70
+ if (!sb3Entry) {
71
+ throw new Error('sb3Entry is not set in TurbowarpPackagerOptions.')
72
+ }
73
+ return {
74
+ environments: {
75
+ turbowarpPackager: {
76
+ build: {
77
+ rolldownOptions: {
78
+ input: VIRTUAL_MODULE_IDS.html,
79
+ output: {
80
+ entryFileNames: 'turbowarp-packager.html',
81
+ },
82
+ },
83
+ },
84
+ consumer: 'client',
85
+ },
86
+ },
87
+ builder: {},
88
+ }
89
+ },
90
+ async buildApp(builder) {
91
+ const env = builder.environments.turbowarpPackager
92
+ if (!env) {
93
+ throw new Error('Turbowarp Packager environment is not configured.')
94
+ }
95
+ await builder.build(env)
96
+ },
97
+ resolveId(source) {
98
+ if (source === VIRTUAL_MODULE_IDS.html) {
99
+ return source
100
+ }
101
+ },
102
+ async load(id) {
103
+ if (id === VIRTUAL_MODULE_IDS.html) {
104
+ const packed = await pack()
105
+ if (!packed) {
106
+ throw new Error('Packing failed.')
107
+ }
108
+
109
+ return {
110
+ code: packed,
111
+ }
112
+ }
113
+ },
114
+ }
115
+ }
@@ -0,0 +1,130 @@
1
+ declare module '@turbowarp/packager' {
2
+ export interface PackagerOptions {
3
+ turbo: boolean
4
+ interpolation: boolean
5
+ framerate: number
6
+ highQualityPen: boolean
7
+ maxClones: number
8
+ fencing: boolean
9
+ miscLimits: boolean
10
+ stageWidth: number
11
+ stageHeight: number
12
+ resizeMode: 'preserve-ratio' | 'stretch' | 'none'
13
+ autoplay: boolean
14
+ username: string
15
+ closeWhenStopped: boolean
16
+ projectId: string
17
+ custom: {
18
+ css: string
19
+ js: string
20
+ }
21
+ appearance: {
22
+ background: string
23
+ foreground: string
24
+ accent: string
25
+ }
26
+ loadingScreen: {
27
+ progressBar: boolean
28
+ text: string
29
+ imageMode: 'normal' | 'stretch'
30
+ image: unknown | null
31
+ }
32
+ controls: {
33
+ greenFlag: {
34
+ enabled: boolean
35
+ }
36
+ stopAll: {
37
+ enabled: boolean
38
+ }
39
+ fullscreen: {
40
+ enabled: boolean
41
+ }
42
+ pause: {
43
+ enabled: boolean
44
+ }
45
+ }
46
+ monitors: {
47
+ editableLists: boolean
48
+ variableColor: string
49
+ listColor: string
50
+ }
51
+ compiler: {
52
+ enabled: boolean
53
+ warpTimer: boolean
54
+ }
55
+ packagedRuntime: boolean
56
+ target:
57
+ | 'html'
58
+ | 'zip'
59
+ | 'zip-one-asset'
60
+ | 'nwjs-win32'
61
+ | 'nwjs-win64'
62
+ | 'nwjs-mac'
63
+ | 'nwjs-linux-x64'
64
+ | 'electron-win32'
65
+ | 'electron-win64'
66
+ | 'electron-win-arm'
67
+ | 'electron-mac'
68
+ | 'electron-linux64'
69
+ | 'electron-linux-arm32'
70
+ | 'electron-linux-arm64'
71
+ | 'webview-mac'
72
+ app: {
73
+ icon: unknown | null
74
+ packageName: string
75
+ windowTitle: string
76
+ windowMode: 'window' | 'maximize' | 'fullscreen'
77
+ version: string
78
+ escapeBehavior:
79
+ | 'unfullscreen-only'
80
+ | 'unfullscreen-or-exit'
81
+ | 'exit-only'
82
+ | 'none'
83
+ windowControls: 'default' | 'frameless'
84
+ backgroundThrottling: boolean
85
+ }
86
+ chunks: {
87
+ gamepad: boolean
88
+ pointerlock: boolean
89
+ }
90
+ cloudVariables: {
91
+ mode: 'ws' | 'local' | 'custom'
92
+ cloudHost: string | string[]
93
+ custom: Record<string, 'ws' | 'local'>
94
+ specialCloudBehaviors: boolean
95
+ unsafeCloudBehaviors: boolean
96
+ }
97
+ cursor: {
98
+ type: 'auto' | 'none' | 'custom'
99
+ custom: unknown | null
100
+ center: {
101
+ x: number
102
+ y: number
103
+ }
104
+ }
105
+ steamworks: {
106
+ appId: string
107
+ onError: 'ignore' | 'warning' | 'error'
108
+ }
109
+ extensions: string[]
110
+ bakeExtensions: boolean
111
+ maxTextureDimension: number
112
+ }
113
+
114
+ export class Packager {
115
+ constructor()
116
+ options: PackagerOptions
117
+ project: unknown
118
+ loadProject(data: ArrayBuffer | Uint8Array | ArrayBufferLike): Promise<void>
119
+ package(): Promise<{ data: Uint8Array | string; type: string }>
120
+ }
121
+
122
+ export function loadProject(
123
+ data: ArrayBuffer | Uint8Array | ArrayBufferLike,
124
+ ): Promise<unknown>
125
+
126
+ export const packager: {
127
+ Packager: typeof Packager
128
+ loadProject: typeof loadProject
129
+ }
130
+ }
@@ -0,0 +1,28 @@
1
+ /// <reference path="./packager.d.ts" />
2
+
3
+ export async function loadPackager() {
4
+ const packagerModule = await import('@turbowarp/packager')
5
+ const Packager =
6
+ packagerModule.Packager ??
7
+ packagerModule.packager?.Packager ??
8
+ (packagerModule as { default: { Packager: unknown } }).default?.Packager ??
9
+ (packagerModule as { default: { packager: { Packager: unknown } } }).default
10
+ ?.packager?.Packager
11
+ const loadProject =
12
+ packagerModule.loadProject ??
13
+ packagerModule.packager?.loadProject ??
14
+ (packagerModule as { default: { loadProject: unknown } }).default
15
+ ?.loadProject ??
16
+ (packagerModule as { default: { packager: { loadProject: unknown } } })
17
+ .default?.packager?.loadProject
18
+
19
+ if (!Packager || !loadProject) {
20
+ throw new Error(
21
+ `Could not find Packager or loadProject in @turbowarp/packager module. Keys: ${Object.keys(
22
+ packagerModule,
23
+ )}`,
24
+ )
25
+ }
26
+
27
+ return { Packager, loadProject }
28
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vite-plus'
2
+
3
+ export default defineConfig({
4
+ pack: {
5
+ entry: 'src/index.ts',
6
+ dts: true,
7
+ },
8
+ })