@idajs/create-mod 0.1.44-dev.51
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/LICENSE +339 -0
- package/README.md +58 -0
- package/_project.config/build.js +107 -0
- package/_project.config/extensions.json +7 -0
- package/_project.config/jsconfig.json +8 -0
- package/_project.config/package.template.json +19 -0
- package/_project.config/run.ps1 +66 -0
- package/_project.config/settings.json +40 -0
- package/_project.config/sync.js +100 -0
- package/_project.config/template.prettierrc.json +11 -0
- package/_project.config/tsconfig.json +21 -0
- package/_project.config/watch.js +76 -0
- package/_project.template/media/README.md +26 -0
- package/_project.template/package.template.json +4 -0
- package/_project.template/src/index.js +63 -0
- package/_project.template/src/index.ts +66 -0
- package/index.js +292 -0
- package/install.js +209 -0
- package/package.json +37 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const os = require("os");
|
|
5
|
+
|
|
6
|
+
// Function to read IdaJS installation directory from .idajs.json
|
|
7
|
+
function getIdaJsPath() {
|
|
8
|
+
// Try local config first, then user home config
|
|
9
|
+
const configPaths = [path.join(__dirname, ".idajs.json"), path.join(os.homedir(), ".idajs.json")];
|
|
10
|
+
|
|
11
|
+
for (const configPath of configPaths) {
|
|
12
|
+
if (fs.existsSync(configPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
15
|
+
if (config.installDir) {
|
|
16
|
+
return config.installDir;
|
|
17
|
+
}
|
|
18
|
+
} catch (err) {
|
|
19
|
+
// Ignore parse errors and try next config
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Get package name from command line argument
|
|
28
|
+
const packageName = process.argv[2];
|
|
29
|
+
|
|
30
|
+
if (!packageName) {
|
|
31
|
+
console.error("Error: Package name is required as an argument");
|
|
32
|
+
console.error("Usage: node sync.js <package-name>");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Get IdaJS installation path
|
|
37
|
+
const idaJsPath = getIdaJsPath();
|
|
38
|
+
if (!idaJsPath) {
|
|
39
|
+
console.error("Error: .idajs.json not found in current directory or user home directory.");
|
|
40
|
+
console.error("Please build IdaJS first or create .idajs.json with 'installDir' property.");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!fs.existsSync(idaJsPath)) {
|
|
45
|
+
console.error(`Error: IdaJS installation directory does not exist: ${idaJsPath}`);
|
|
46
|
+
console.error("Please build IdaJS first.");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isTypeScriptProject = (() => {
|
|
51
|
+
// Check if tsconfig.json exists
|
|
52
|
+
if (!fs.existsSync("tsconfig.json")) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if there are any .ts files in src/
|
|
57
|
+
if (!fs.existsSync("src")) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const files = fs.readdirSync("src");
|
|
62
|
+
return files.some((file) => file.endsWith(".ts"));
|
|
63
|
+
})();
|
|
64
|
+
|
|
65
|
+
const targetBase = path.join(idaJsPath, "GameRun", "mods", packageName);
|
|
66
|
+
const deleteExcludeArgs = '--delete-exclude "*.ida" --delete-exclude "*.md5"';
|
|
67
|
+
|
|
68
|
+
console.log(`Syncing ${packageName}...`);
|
|
69
|
+
|
|
70
|
+
// If TypeScript project, compile first
|
|
71
|
+
if (isTypeScriptProject) {
|
|
72
|
+
console.log("TypeScript project detected, compiling...");
|
|
73
|
+
|
|
74
|
+
// Clean dist folder
|
|
75
|
+
if (fs.existsSync("dist")) {
|
|
76
|
+
console.log("Cleaning dist folder...");
|
|
77
|
+
fs.rmSync("dist", { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Compile TypeScript
|
|
81
|
+
execSync("tsc", { stdio: "inherit" });
|
|
82
|
+
console.log("Compilation complete!");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Sync media folder if it exists
|
|
86
|
+
if (fs.existsSync("media")) {
|
|
87
|
+
const mediaSyncCmd = `idasync media ${path.join(targetBase, "media")} ${deleteExcludeArgs}`;
|
|
88
|
+
console.log(`Running: ${mediaSyncCmd}`);
|
|
89
|
+
execSync(mediaSyncCmd, { stdio: "inherit" });
|
|
90
|
+
} else {
|
|
91
|
+
console.log("No media folder found, skipping media sync");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sync src folder
|
|
95
|
+
const sourceFolder = isTypeScriptProject ? "dist" : "src";
|
|
96
|
+
const srcSyncCmd = `idasync ${sourceFolder} ${targetBase} ${deleteExcludeArgs} --delete-exclude "media/*"`;
|
|
97
|
+
console.log(`Running: ${srcSyncCmd}`);
|
|
98
|
+
execSync(srcSyncCmd, { stdio: "inherit" });
|
|
99
|
+
|
|
100
|
+
console.log("Sync completed!");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2021",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"lib": ["ES2021", "DOM"],
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"removeComments": true,
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"checkJs": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"types": ["@idajs/types"],
|
|
14
|
+
"typeRoots": ["./node_modules/@types", "./node_modules"],
|
|
15
|
+
"moduleResolution": "node",
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*.ts", "src/**/*.js"],
|
|
20
|
+
"exclude": ["node_modules"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watches for file changes in the src/ and media/ directories
|
|
3
|
+
* and runs the sync script to copy changed files to the game mod directory.
|
|
4
|
+
* Exits when the LBA2.exe process is no longer running.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chokidar = require("chokidar");
|
|
8
|
+
const { spawn, exec } = require("child_process");
|
|
9
|
+
const { promisify } = require("util");
|
|
10
|
+
|
|
11
|
+
const execAsync = promisify(exec);
|
|
12
|
+
const PROC_NAME = "LBA2.exe";
|
|
13
|
+
|
|
14
|
+
let syncing = null;
|
|
15
|
+
function runSync() {
|
|
16
|
+
if (syncing) return; // prevent overlapping syncs
|
|
17
|
+
syncing = spawn("npm", ["run", "sync"], { stdio: "inherit", shell: true });
|
|
18
|
+
syncing.on("exit", () => (syncing = null));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const watcher = chokidar.watch(["src/**/*.js", "media/**/*.png"], {
|
|
22
|
+
ignoreInitial: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
watcher.on("all", () => runSync());
|
|
26
|
+
|
|
27
|
+
async function isProcRunning() {
|
|
28
|
+
try {
|
|
29
|
+
const { stdout } = await execAsync(
|
|
30
|
+
`powershell -Command "Get-Process | Where-Object {$_.ProcessName -eq '${PROC_NAME.replace(".exe", "")}'} | Select-Object -First 1"`,
|
|
31
|
+
{
|
|
32
|
+
timeout: 5000,
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
return stdout.trim().length > 0;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
// Process not found or command failed
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function killGameProc() {
|
|
43
|
+
try {
|
|
44
|
+
await execAsync(
|
|
45
|
+
`powershell -Command "Stop-Process -Name '${PROC_NAME.replace(".exe", "")}' -Force -ErrorAction SilentlyContinue"`
|
|
46
|
+
);
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// Process may already be stopped
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const interval = setInterval(async () => {
|
|
53
|
+
try {
|
|
54
|
+
const alive = await isProcRunning();
|
|
55
|
+
if (!alive) {
|
|
56
|
+
console.log(`${PROC_NAME} not running. Exiting watcher.`);
|
|
57
|
+
|
|
58
|
+
clearInterval(interval);
|
|
59
|
+
await watcher.close();
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
// if process listing fails, you can decide to ignore or stop
|
|
64
|
+
}
|
|
65
|
+
}, 1000);
|
|
66
|
+
|
|
67
|
+
// Ctrl+C cleanup
|
|
68
|
+
process.on("SIGINT", async () => {
|
|
69
|
+
console.log("Caught interrupt signal. Exiting watcher.");
|
|
70
|
+
|
|
71
|
+
clearInterval(interval);
|
|
72
|
+
await watcher.close();
|
|
73
|
+
await killGameProc();
|
|
74
|
+
|
|
75
|
+
process.exit(0);
|
|
76
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Media Assets
|
|
2
|
+
|
|
3
|
+
This folder contains custom media assets for your mod.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
You can create `images` and `sprites` folders here to have custom PNG files for your IdaJS mod:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
media/
|
|
11
|
+
├── images/
|
|
12
|
+
│ ├── my-dialog-bg.png
|
|
13
|
+
│ ├── cutscene-01.png
|
|
14
|
+
│ └── ...
|
|
15
|
+
└── sprites/
|
|
16
|
+
├── item-icon.png
|
|
17
|
+
├── character-portrait.png
|
|
18
|
+
└── ...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
- **`images/`** - Full-screen dialog backgrounds and standalone images
|
|
24
|
+
- **`sprites/`** - In-dialog pictures and smaller graphics used within dialogs
|
|
25
|
+
|
|
26
|
+
All PNG files will be automatically synced to your IdaJS installation when you run `npm start` or `npm run sync`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welcome to IdaJS modding!
|
|
3
|
+
* Read the documentation here: https://ida.innerbytes.com
|
|
4
|
+
*/
|
|
5
|
+
console.log("Welcome to the IdaJS project!\n");
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Start with this event handler to setup every scene, the mod needs to modify
|
|
9
|
+
*/
|
|
10
|
+
scene.addEventListener(scene.Events.afterLoadScene, (sceneId, sceneLoadMode) => {
|
|
11
|
+
// Setup initial behavior for the Twinsen house scene here
|
|
12
|
+
if (sceneId === 0) {
|
|
13
|
+
// Do something with Twinsen and Zoe objects here.
|
|
14
|
+
// See Samples for mod examples
|
|
15
|
+
const twinsen = scene.getObject(0);
|
|
16
|
+
const zoe = scene.getObject(4);
|
|
17
|
+
|
|
18
|
+
// Handle move script if you will run custom coroutines on the object
|
|
19
|
+
// twinsen.handleMoveScript();
|
|
20
|
+
|
|
21
|
+
// Handle life script if you want to have custom behavior on the object
|
|
22
|
+
// twinsen.handleLifeScript(myLifeScriptFunction);
|
|
23
|
+
|
|
24
|
+
// Register your coroutines
|
|
25
|
+
registerCoroutine("myCoroutine", myCoroutineFunction);
|
|
26
|
+
|
|
27
|
+
// Do more setup - add and modify Zones, Waypoints, Objects, etc - as needed
|
|
28
|
+
|
|
29
|
+
// If new game started, do initialization of your game variables here as well, as needed:
|
|
30
|
+
if (sceneLoadMode === scene.LoadModes.NewGameStarted) {
|
|
31
|
+
const gameStore = useGameStore();
|
|
32
|
+
gameStore.myVariable = "some value";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If it's any other load mode, except loading from saved state, do scene state initialization here - init scene variables, start some coroutines, that should run from scene start, etc
|
|
36
|
+
if (sceneLoadMode !== scene.LoadModes.WillLoadSavedState) {
|
|
37
|
+
const sceneStore = useSceneStore();
|
|
38
|
+
sceneStore.mySceneVariable = 42;
|
|
39
|
+
|
|
40
|
+
// Start the previously registered "myCoroutine" on object 0 (Twinsen), with some arguments
|
|
41
|
+
// startCoroutine(0, "myCoroutine", 1, 2, 3, 4);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Setup initial behavior for the outside Twinsen house scene here
|
|
45
|
+
else if (sceneId === 49) {
|
|
46
|
+
// Scene setup
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Etc
|
|
50
|
+
// Look at https://lba2remake.net to see the needed scene ids
|
|
51
|
+
// Teleport to the desired scene in the Editor, the id will be shown in the inspector
|
|
52
|
+
/*
|
|
53
|
+
else if (sceneId === 1) {
|
|
54
|
+
// Scene setup
|
|
55
|
+
}
|
|
56
|
+
*/
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Example of a coroutine function with some arguments
|
|
60
|
+
function* myCoroutineFunction(arg1, arg2, arg3, etc) {
|
|
61
|
+
// Your coroutine code goes here
|
|
62
|
+
yield doMove(ida.Move.TM_ANIM, 0);
|
|
63
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SceneLoadMode } from "@idajs/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Welcome to IdaJS modding!
|
|
5
|
+
* Read the documentation here: https://ida.innerbytes.com
|
|
6
|
+
*/
|
|
7
|
+
console.log("Welcome to the IdaJS project!\n");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Start with this event handler to setup every scene, the mod needs to modify
|
|
11
|
+
*/
|
|
12
|
+
scene.addEventListener(
|
|
13
|
+
scene.Events.afterLoadScene,
|
|
14
|
+
(sceneId: number, sceneLoadMode: SceneLoadMode) => {
|
|
15
|
+
// Setup initial behavior for the Twinsen house scene here
|
|
16
|
+
if (sceneId === 0) {
|
|
17
|
+
// Do something with Twinsen and Zoe objects here.
|
|
18
|
+
// See Samples for mod examples
|
|
19
|
+
const twinsen = scene.getObject(0);
|
|
20
|
+
const zoe = scene.getObject(4);
|
|
21
|
+
|
|
22
|
+
// Handle move script if you will run custom coroutines on the object
|
|
23
|
+
// twinsen.handleMoveScript();
|
|
24
|
+
|
|
25
|
+
// Handle life script if you want to have custom behavior on the object
|
|
26
|
+
// twinsen.handleLifeScript(myLifeScriptFunction);
|
|
27
|
+
|
|
28
|
+
// Register your coroutines
|
|
29
|
+
registerCoroutine("myCoroutine", myCoroutineFunction);
|
|
30
|
+
|
|
31
|
+
// Do more setup - add and modify Zones, Waypoints, Objects, etc - as needed
|
|
32
|
+
|
|
33
|
+
// If new game started, do initialization of your game variables here as well, as needed:
|
|
34
|
+
if (sceneLoadMode === scene.LoadModes.NewGameStarted) {
|
|
35
|
+
const gameStore = useGameStore();
|
|
36
|
+
gameStore.myVariable = "some value";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If it's any other load mode, except loading from saved state, do scene state initialization here - init scene variables, start some coroutines, that should run from scene start, etc
|
|
40
|
+
if (sceneLoadMode !== scene.LoadModes.WillLoadSavedState) {
|
|
41
|
+
const sceneStore = useSceneStore();
|
|
42
|
+
sceneStore.mySceneVariable = 42;
|
|
43
|
+
|
|
44
|
+
// Start the previously registered "myCoroutine" on object 0 (Twinsen), with some arguments
|
|
45
|
+
// startCoroutine(0, "myCoroutine", 1, true, "three", [4]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Setup initial behavior for the outside Twinsen house scene here
|
|
49
|
+
else if (sceneId === 49) {
|
|
50
|
+
// Scene setup
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Etc
|
|
54
|
+
// Look at https://lba2remake.net to see the needed scene ids
|
|
55
|
+
// Teleport to the desired scene in the Editor, the id will be shown in the inspector
|
|
56
|
+
// else if (sceneId === <neededSceneId>) {
|
|
57
|
+
// // Scene setup
|
|
58
|
+
// }
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Example of a coroutine function with some arguments
|
|
63
|
+
function* myCoroutineFunction(arg1: number, arg2: boolean, arg3: string, etc: number[]) {
|
|
64
|
+
// Your coroutine code goes here
|
|
65
|
+
yield doMove(ida.Move.TM_ANIM, 0);
|
|
66
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
const { execSync } = require("child_process");
|
|
7
|
+
const IdaSync = require("@idajs/sync");
|
|
8
|
+
|
|
9
|
+
// Parse command line arguments
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
|
|
12
|
+
// Helper to get argument value
|
|
13
|
+
function getArgValue(argName) {
|
|
14
|
+
const index = args.findIndex((arg) => arg === argName || arg.startsWith(`${argName}=`));
|
|
15
|
+
if (index === -1) return null;
|
|
16
|
+
const arg = args[index];
|
|
17
|
+
if (arg.includes("=")) {
|
|
18
|
+
return arg.split("=")[1];
|
|
19
|
+
}
|
|
20
|
+
return args[index + 1];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const projectName = args.find((arg) => !arg.startsWith("--"));
|
|
24
|
+
const targetDirectory = getArgValue("--dir") || getArgValue("--directory");
|
|
25
|
+
const idajsDir = getArgValue("--idajs-dir") || getArgValue("--idajs");
|
|
26
|
+
const useTypeScript = args.includes("--typescript") || args.includes("--ts");
|
|
27
|
+
const useJavaScript = args.includes("--javascript") || args.includes("--js");
|
|
28
|
+
const skipInstall = args.includes("--skip-install");
|
|
29
|
+
const helpRequested = args.includes("--help") || args.includes("-h");
|
|
30
|
+
|
|
31
|
+
// Show help if requested
|
|
32
|
+
if (helpRequested) {
|
|
33
|
+
console.log(`
|
|
34
|
+
Usage: npx @idajs/create-mod [project-name] [options]
|
|
35
|
+
|
|
36
|
+
Arguments:
|
|
37
|
+
project-name Name of your mod project (default: my-ida-mod)
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--dir, --directory <path> Target directory (default: project name)
|
|
41
|
+
--idajs-dir, --idajs <path> IdaJS installation directory
|
|
42
|
+
--typescript, --ts Use TypeScript
|
|
43
|
+
--javascript, --js Use JavaScript
|
|
44
|
+
--skip-install Skip npm install
|
|
45
|
+
-h, --help Show this help message
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
npx @idajs/create-mod
|
|
49
|
+
npx @idajs/create-mod my-awesome-mod
|
|
50
|
+
npx @idajs/create-mod my-mod --typescript
|
|
51
|
+
npx @idajs/create-mod my-mod --dir ./projects/my-mod --idajs C:/Projects/idajs --js
|
|
52
|
+
npx @idajs/create-mod my-mod --skip-install
|
|
53
|
+
`);
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Function to read IdaJS directory from config file
|
|
58
|
+
function getIdaJsDirFromConfig() {
|
|
59
|
+
const configPath = path.join(os.homedir(), ".idajs.json");
|
|
60
|
+
try {
|
|
61
|
+
if (fs.existsSync(configPath)) {
|
|
62
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
63
|
+
if (config.installDir) {
|
|
64
|
+
return config.installDir;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// Ignore errors, will prompt user
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function main() {
|
|
74
|
+
console.log("Welcome to IdaJS Mod Creator!\n");
|
|
75
|
+
|
|
76
|
+
// Determine if we need prompts
|
|
77
|
+
const needsPrompts =
|
|
78
|
+
!projectName || (!useTypeScript && !useJavaScript) || (!idajsDir && !getIdaJsDirFromConfig());
|
|
79
|
+
|
|
80
|
+
let config = {
|
|
81
|
+
projectName: projectName || "my-ida-mod",
|
|
82
|
+
targetDirectory: targetDirectory,
|
|
83
|
+
idajsDir: idajsDir,
|
|
84
|
+
language: useTypeScript ? "ts" : useJavaScript ? "js" : null,
|
|
85
|
+
install: !skipInstall,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Try to get IdaJS directory from config if not provided via CLI
|
|
89
|
+
if (!config.idajsDir) {
|
|
90
|
+
const configDir = getIdaJsDirFromConfig();
|
|
91
|
+
if (configDir) {
|
|
92
|
+
config.idajsDir = configDir;
|
|
93
|
+
console.log(`Found IdaJS installation: ${configDir}\n`);
|
|
94
|
+
} else {
|
|
95
|
+
console.log("⚠️ Warning: ~/.idajs.json not found. Make sure you have built IdaJS first.\n");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Use prompts only if needed
|
|
100
|
+
if (needsPrompts) {
|
|
101
|
+
const prompts = require("prompts");
|
|
102
|
+
|
|
103
|
+
const questions = [];
|
|
104
|
+
|
|
105
|
+
if (!projectName) {
|
|
106
|
+
questions.push({
|
|
107
|
+
type: "text",
|
|
108
|
+
name: "projectName",
|
|
109
|
+
message: "Project name:",
|
|
110
|
+
initial: "my-ida-mod",
|
|
111
|
+
validate: (value) => (value.length > 0 ? true : "Project name cannot be empty"),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!config.targetDirectory) {
|
|
116
|
+
questions.push({
|
|
117
|
+
type: "text",
|
|
118
|
+
name: "targetDirectory",
|
|
119
|
+
message: "Target directory:",
|
|
120
|
+
initial: (prev) => prev || config.projectName,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!config.idajsDir) {
|
|
125
|
+
questions.push({
|
|
126
|
+
type: "text",
|
|
127
|
+
name: "idajsDir",
|
|
128
|
+
message: "IdaJS installation directory:",
|
|
129
|
+
validate: (value) => {
|
|
130
|
+
if (!value || value.length === 0) {
|
|
131
|
+
return "IdaJS installation directory is required";
|
|
132
|
+
}
|
|
133
|
+
if (!fs.existsSync(value)) {
|
|
134
|
+
return `Directory does not exist: ${value}`;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!config.language) {
|
|
142
|
+
questions.push({
|
|
143
|
+
type: "select",
|
|
144
|
+
name: "language",
|
|
145
|
+
message: "Select language:",
|
|
146
|
+
choices: [
|
|
147
|
+
{ title: "JavaScript", value: "js" },
|
|
148
|
+
{ title: "TypeScript", value: "ts" },
|
|
149
|
+
],
|
|
150
|
+
initial: 0,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const response = await prompts(questions, {
|
|
155
|
+
onCancel: () => {
|
|
156
|
+
console.log("\nCancelled by user");
|
|
157
|
+
process.exit(1);
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
config = { ...config, ...response };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Set target directory to project name if not specified
|
|
165
|
+
if (!config.targetDirectory) {
|
|
166
|
+
config.targetDirectory = config.projectName;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const targetDir = path.isAbsolute(config.targetDirectory)
|
|
170
|
+
? config.targetDirectory
|
|
171
|
+
: path.join(process.cwd(), config.targetDirectory);
|
|
172
|
+
|
|
173
|
+
// Validate IdaJS directory exists
|
|
174
|
+
if (!fs.existsSync(config.idajsDir)) {
|
|
175
|
+
console.error(`\n❌ Error: IdaJS installation directory does not exist: ${config.idajsDir}`);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check if directory already exists
|
|
180
|
+
if (fs.existsSync(targetDir)) {
|
|
181
|
+
console.error(`\n❌ Error: Directory already exists: ${targetDir}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
console.log(`\nCreating IdaJS mod in ${targetDir}...`);
|
|
186
|
+
console.log(`Language: ${config.language === "js" ? "JavaScript" : "TypeScript"}`);
|
|
187
|
+
console.log(`IdaJS: ${config.idajsDir}\n`);
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
// Create project directory
|
|
191
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
192
|
+
|
|
193
|
+
// Get paths
|
|
194
|
+
const templatesDir = path.join(__dirname, "_project.template");
|
|
195
|
+
|
|
196
|
+
// Copy all files and folders from _project.template using IdaSync
|
|
197
|
+
console.log("Copying template files...");
|
|
198
|
+
|
|
199
|
+
// Exclude files based on selected language
|
|
200
|
+
const copyExclusions =
|
|
201
|
+
config.language === "js"
|
|
202
|
+
? ["src/index.ts", "tsconfig.json"]
|
|
203
|
+
: ["src/index.js", "jsconfig.json"];
|
|
204
|
+
|
|
205
|
+
const sync = new IdaSync({
|
|
206
|
+
verbose: false,
|
|
207
|
+
copyExclusions: copyExclusions,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await sync.sync(templatesDir, targetDir);
|
|
211
|
+
console.log("✓ Copied template structure");
|
|
212
|
+
|
|
213
|
+
// Modify package.template.json
|
|
214
|
+
const packageTemplatePath = path.join(targetDir, "package.template.json");
|
|
215
|
+
const packageTemplate = JSON.parse(fs.readFileSync(packageTemplatePath, "utf8"));
|
|
216
|
+
packageTemplate.name = config.projectName;
|
|
217
|
+
packageTemplate.description = "This is a new IdaJS project";
|
|
218
|
+
fs.writeFileSync(packageTemplatePath, JSON.stringify(packageTemplate, null, 2) + "\n");
|
|
219
|
+
console.log("✓ Updated package.template.json");
|
|
220
|
+
|
|
221
|
+
// Create .idajs.json config file
|
|
222
|
+
const idajsConfig = {
|
|
223
|
+
installDir: config.idajsDir,
|
|
224
|
+
};
|
|
225
|
+
fs.writeFileSync(
|
|
226
|
+
path.join(targetDir, ".idajs.json"),
|
|
227
|
+
JSON.stringify(idajsConfig, null, 2) + "\n"
|
|
228
|
+
);
|
|
229
|
+
console.log("✓ Created .idajs.json");
|
|
230
|
+
|
|
231
|
+
// Call Samples/install.js to set up the project
|
|
232
|
+
console.log("\nSetting up development environment...");
|
|
233
|
+
const installScript = path.join(__dirname, "install.js");
|
|
234
|
+
const installArgs = [installScript, targetDir];
|
|
235
|
+
|
|
236
|
+
if (!config.install) {
|
|
237
|
+
installArgs.push("--skip-install");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
execSync(`node ${installArgs.map((arg) => `"${arg}"`).join(" ")}`, {
|
|
241
|
+
stdio: "inherit",
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Post-process package.json to remove setup script, author, and private properties
|
|
245
|
+
const finalPackageJsonPath = path.join(targetDir, "package.json");
|
|
246
|
+
const finalPackageJson = JSON.parse(fs.readFileSync(finalPackageJsonPath, "utf8"));
|
|
247
|
+
|
|
248
|
+
if (finalPackageJson.scripts && finalPackageJson.scripts.setup) {
|
|
249
|
+
delete finalPackageJson.scripts.setup;
|
|
250
|
+
}
|
|
251
|
+
if (finalPackageJson.author !== undefined) {
|
|
252
|
+
delete finalPackageJson.author;
|
|
253
|
+
}
|
|
254
|
+
if (finalPackageJson.private !== undefined) {
|
|
255
|
+
delete finalPackageJson.private;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Remove TypeScript from devDependencies if it's a JavaScript project
|
|
259
|
+
if (
|
|
260
|
+
config.language === "js" &&
|
|
261
|
+
finalPackageJson.devDependencies &&
|
|
262
|
+
finalPackageJson.devDependencies.typescript
|
|
263
|
+
) {
|
|
264
|
+
delete finalPackageJson.devDependencies.typescript;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
fs.writeFileSync(finalPackageJsonPath, JSON.stringify(finalPackageJson, null, 2) + "\n");
|
|
268
|
+
console.log("✓ Configured package.json");
|
|
269
|
+
|
|
270
|
+
// Remove package.template.json as it's no longer needed
|
|
271
|
+
fs.unlinkSync(packageTemplatePath);
|
|
272
|
+
console.log("✓ Cleaned up template files");
|
|
273
|
+
|
|
274
|
+
// Success message
|
|
275
|
+
console.log(`\n✅ Successfully created "${config.projectName}"!\n`);
|
|
276
|
+
console.log("Next steps:");
|
|
277
|
+
console.log(` cd ${path.relative(process.cwd(), targetDir) || config.targetDirectory}`);
|
|
278
|
+
if (!config.install) {
|
|
279
|
+
console.log(" npm install");
|
|
280
|
+
}
|
|
281
|
+
console.log(" npm start\n");
|
|
282
|
+
console.log("Happy modding! 🎮");
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error(`\n❌ Error creating project: ${error.message}`);
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
main().catch((error) => {
|
|
290
|
+
console.error("Unexpected error:", error);
|
|
291
|
+
process.exit(1);
|
|
292
|
+
});
|