@iruka-edu/create-iruka-game 1.0.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/dist/index.cjs +114 -0
- package/package.json +33 -0
- package/templates/basic-phaser/index.html +12 -0
- package/templates/basic-phaser/package.json +18 -0
- package/templates/basic-phaser/src/main.ts +30 -0
- package/templates/basic-phaser/tsconfig.json +23 -0
- package/templates/basic-phaser/vite.config.ts +7 -0
- package/templates/react-phaser/README.md +28 -0
- package/templates/react-phaser/index.html +12 -0
- package/templates/react-phaser/iruka.config.json +5 -0
- package/templates/react-phaser/package.json +23 -0
- package/templates/react-phaser/scripts/sdk-check.mjs +5 -0
- package/templates/react-phaser/src/MyGame.ts +24 -0
- package/templates/react-phaser/src/main.tsx +29 -0
- package/templates/react-phaser/tsconfig.json +12 -0
- package/templates/react-phaser/vite.config.ts +7 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_fs_extra = __toESM(require("fs-extra"), 1);
|
|
28
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
29
|
+
var import_prompts = __toESM(require("prompts"), 1);
|
|
30
|
+
var import_node_child_process = require("child_process");
|
|
31
|
+
var import_picocolors = __toESM(require("picocolors"), 1);
|
|
32
|
+
async function init() {
|
|
33
|
+
console.log(import_picocolors.default.cyan("\u{1F680} Iruka Game Starter CLI"));
|
|
34
|
+
const response = await (0, import_prompts.default)([
|
|
35
|
+
// 1. Hỏi tên project
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
name: "projectName",
|
|
39
|
+
message: "T\xEAn th\u01B0 m\u1EE5c game:",
|
|
40
|
+
initial: "my-iruka-game"
|
|
41
|
+
},
|
|
42
|
+
// 2. [MỚI] Hỏi loại Template
|
|
43
|
+
{
|
|
44
|
+
type: "select",
|
|
45
|
+
name: "templateType",
|
|
46
|
+
message: "Ch\u1ECDn lo\u1EA1i Project:",
|
|
47
|
+
choices: [
|
|
48
|
+
{
|
|
49
|
+
title: "Basic Phaser (Phaser.js only)",
|
|
50
|
+
description: "Nh\u1EC7, d\xF9ng @iruka-edu/game-core. Ph\xF9 h\u1EE3p game \u0111\u01A1n gi\u1EA3n.",
|
|
51
|
+
value: "basic-phaser"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: "React + Phaser (Recommended)",
|
|
55
|
+
description: "M\u1EA1nh m\u1EBD, d\xF9ng @iruka-edu/mini-game-sdk. D\u1EC5 l\xE0m UI.",
|
|
56
|
+
value: "react-phaser"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
initial: 1
|
|
60
|
+
},
|
|
61
|
+
// 3. Hỏi Token
|
|
62
|
+
{
|
|
63
|
+
type: "password",
|
|
64
|
+
// Ẩn token khi nhập
|
|
65
|
+
name: "token",
|
|
66
|
+
message: "Nh\u1EADp GitHub Token (\u0111\u1EC3 c\xE0i \u0111\u1EB7t SDK):",
|
|
67
|
+
validate: (value) => value.length < 10 ? "Token kh\xF4ng h\u1EE3p l\u1EC7" : true
|
|
68
|
+
}
|
|
69
|
+
]);
|
|
70
|
+
const { projectName, templateType, token } = response;
|
|
71
|
+
if (!projectName || !templateType || !token) process.exit(1);
|
|
72
|
+
const root = import_node_path.default.join(process.cwd(), projectName);
|
|
73
|
+
const templateDir = import_node_path.default.resolve(__dirname, `../templates/${templateType}`);
|
|
74
|
+
if (!import_fs_extra.default.existsSync(templateDir)) {
|
|
75
|
+
console.error(import_picocolors.default.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y template: ${templateType}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (import_fs_extra.default.existsSync(root)) {
|
|
79
|
+
console.error(import_picocolors.default.red(`\u274C Th\u01B0 m\u1EE5c ${projectName} \u0111\xE3 t\u1ED3n t\u1EA1i!`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
console.log(
|
|
83
|
+
import_picocolors.default.blue(`
|
|
84
|
+
\u{1F4C2} \u0110ang kh\u1EDFi t\u1EA1o project t\u1EEB template: ${templateType}...`)
|
|
85
|
+
);
|
|
86
|
+
import_fs_extra.default.copySync(templateDir, root);
|
|
87
|
+
const pkgPath = import_node_path.default.join(root, "package.json");
|
|
88
|
+
const pkg = import_fs_extra.default.readJsonSync(pkgPath);
|
|
89
|
+
pkg.name = projectName;
|
|
90
|
+
import_fs_extra.default.writeJsonSync(pkgPath, pkg, { spaces: 2 });
|
|
91
|
+
console.log(import_picocolors.default.blue("\u{1F511} \u0110ang c\u1EA5u h\xECnh x\xE1c th\u1EF1c..."));
|
|
92
|
+
const npmrcContent = `
|
|
93
|
+
@iruka-edu:registry=https://npm.pkg.github.com
|
|
94
|
+
//npm.pkg.github.com/:_authToken=${token}
|
|
95
|
+
`;
|
|
96
|
+
import_fs_extra.default.writeFileSync(import_node_path.default.join(root, ".npmrc"), npmrcContent.trim());
|
|
97
|
+
console.log(import_picocolors.default.blue("\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t th\u01B0 vi\u1EC7n..."));
|
|
98
|
+
try {
|
|
99
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
100
|
+
const pkgManager = userAgent.startsWith("pnpm") ? "pnpm" : "npm";
|
|
101
|
+
(0, import_node_child_process.execSync)(`${pkgManager} install`, { cwd: root, stdio: "inherit" });
|
|
102
|
+
(0, import_node_child_process.execSync)("git init", { cwd: root, stdio: "ignore" });
|
|
103
|
+
console.log(import_picocolors.default.green(`
|
|
104
|
+
\u2705 Th\xE0nh c\xF4ng! Game ${templateType} \u0111\xE3 s\u1EB5n s\xE0ng.`));
|
|
105
|
+
console.log(`
|
|
106
|
+
\u{1F449} Ch\u1EA1y l\u1EC7nh sau \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u:
|
|
107
|
+
`);
|
|
108
|
+
console.log(import_picocolors.default.cyan(` cd ${projectName}`));
|
|
109
|
+
console.log(import_picocolors.default.cyan(` ${pkgManager} run dev`));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(import_picocolors.default.red("\u274C L\u1ED7i c\xE0i \u0111\u1EB7t. Ki\u1EC3m tra l\u1EA1i Token."));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
init().catch((e) => console.error(e));
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@iruka-edu/create-iruka-game",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create Iruka mini game (React + Phaser template)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-iruka-game": "dist/index.cjs"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup src/index.ts --format cjs --clean",
|
|
12
|
+
"dev": "tsup src/index.ts --format cjs --watch"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"fs-extra": "^11.3.3",
|
|
16
|
+
"picocolors": "^1.1.1",
|
|
17
|
+
"prompts": "^2.4.2"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/fs-extra": "^11.0.4",
|
|
21
|
+
"@types/prompts": "^2.4.9",
|
|
22
|
+
"tsup": "^8.0.0",
|
|
23
|
+
"typescript": "^5.5.0"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"templates"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"registry": "https://registry.npmjs.org/"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Basic Phaser Game</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="game-container"></div>
|
|
10
|
+
<script type="module" src="/src/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "basic-phaser-template",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"phaser": "^3.60.0",
|
|
12
|
+
"@iruka-edu/game-core": "latest"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"typescript": "^5.0.0",
|
|
16
|
+
"vite": "^4.0.0"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Phaser from "phaser";
|
|
2
|
+
|
|
3
|
+
class DemoScene extends Phaser.Scene {
|
|
4
|
+
constructor() {
|
|
5
|
+
super("DemoScene");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
preload() {
|
|
9
|
+
// Preload assets here
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
create() {
|
|
13
|
+
this.add
|
|
14
|
+
.text(400, 300, "Iruka Basic Phaser Template", {
|
|
15
|
+
fontSize: "32px",
|
|
16
|
+
color: "#00ff00",
|
|
17
|
+
})
|
|
18
|
+
.setOrigin(0.5);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const config: Phaser.Types.Core.GameConfig = {
|
|
23
|
+
type: Phaser.AUTO,
|
|
24
|
+
width: 800,
|
|
25
|
+
height: 600,
|
|
26
|
+
scene: DemoScene,
|
|
27
|
+
parent: "game-container",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
new Phaser.Game(config);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noUnusedLocals": true,
|
|
15
|
+
"noUnusedParameters": true,
|
|
16
|
+
"noImplicitReturns": true,
|
|
17
|
+
"allowJs": true,
|
|
18
|
+
"checkJs": false,
|
|
19
|
+
"esModuleInterop": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true
|
|
21
|
+
},
|
|
22
|
+
"include": ["src"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# **IRUKA_GAME_NAME**
|
|
2
|
+
|
|
3
|
+
An Iruka mini game built with React + Phaser.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
pnpm install
|
|
10
|
+
|
|
11
|
+
# Start development server
|
|
12
|
+
pnpm dev
|
|
13
|
+
|
|
14
|
+
# Build for production
|
|
15
|
+
pnpm build
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Update SDK
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm up @iruka-edu/mini-game-sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Check SDK version
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm sdk:check
|
|
28
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="vi">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>__IRUKA_GAME_NAME__</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-phaser-template",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"react": "^18.2.0",
|
|
12
|
+
"react-dom": "^18.2.0",
|
|
13
|
+
"phaser": "^3.60.0",
|
|
14
|
+
"@iruka-edu/mini-game-sdk": "latest"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/react": "^18.0.0",
|
|
18
|
+
"@types/react-dom": "^18.0.0",
|
|
19
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
20
|
+
"typescript": "^5.0.0",
|
|
21
|
+
"vite": "^4.0.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import pkg from "../package.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
const v = pkg.dependencies?.["@iruka/mini-game-sdk"];
|
|
4
|
+
console.log(`[Iruka] SDK dependency: @iruka/mini-game-sdk = ${v || "missing"}`);
|
|
5
|
+
console.log(`[Iruka] Tip: run "pnpm up @iruka/mini-game-sdk" to update.`);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseGame, type GameRuntimeConfig } from "@iruka/mini-game-sdk";
|
|
2
|
+
|
|
3
|
+
export class MyGame extends BaseGame {
|
|
4
|
+
private score = 0;
|
|
5
|
+
|
|
6
|
+
async onInit(cfg: GameRuntimeConfig) {
|
|
7
|
+
super.onInit(cfg);
|
|
8
|
+
const root = document.getElementById("game-root")!;
|
|
9
|
+
root.innerHTML = `
|
|
10
|
+
<div style="padding:24px;font:16px/1.4 system-ui">
|
|
11
|
+
<h2>🐳 __IRUKA_GAME_NAME__</h2>
|
|
12
|
+
<p>Locale: ${cfg.locale} • Difficulty: ${cfg.difficulty}</p>
|
|
13
|
+
<button id="btn-score">+1 điểm</button>
|
|
14
|
+
</div>
|
|
15
|
+
`;
|
|
16
|
+
document.getElementById("btn-score")?.addEventListener("click", () => {
|
|
17
|
+
this.score++;
|
|
18
|
+
(window as any).__irukaBridge?.score?.(this.score, 1);
|
|
19
|
+
this.telemetry({ type: "score", data: { score: this.score } });
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const createGame = () => new MyGame();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import {
|
|
4
|
+
IrukaGameHost,
|
|
5
|
+
createIframeBridge,
|
|
6
|
+
type HostCommand,
|
|
7
|
+
} from "@iruka-edu/mini-game-sdk";
|
|
8
|
+
import { createGame } from "./MyGame";
|
|
9
|
+
|
|
10
|
+
const bridge = createIframeBridge((cmd: HostCommand) => {
|
|
11
|
+
console.log("[Hub→Game]", cmd);
|
|
12
|
+
}, "*");
|
|
13
|
+
(window as any).__irukaBridge = bridge;
|
|
14
|
+
|
|
15
|
+
const App = () => (
|
|
16
|
+
<IrukaGameHost
|
|
17
|
+
createGame={createGame}
|
|
18
|
+
config={{
|
|
19
|
+
runtime: "iframe-html",
|
|
20
|
+
locale: "vi",
|
|
21
|
+
difficulty: 2,
|
|
22
|
+
seed: 123,
|
|
23
|
+
player: { userId: "demo" },
|
|
24
|
+
hubApi: {} as any,
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
createRoot(document.getElementById("root")!).render(<App />);
|