@next2d/builder 0.1.11 → 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.
Files changed (54) hide show
  1. package/README.md +90 -57
  2. package/dist/cli.d.ts +7 -0
  3. package/dist/cli.js +68 -3
  4. package/dist/colors.d.ts +6 -0
  5. package/dist/colors.js +7 -0
  6. package/dist/context.d.ts +10 -1
  7. package/dist/context.js +7 -0
  8. package/dist/description.d.ts +2 -0
  9. package/dist/description.js +8 -0
  10. package/dist/electron-config.d.ts +62 -0
  11. package/dist/electron-config.js +134 -0
  12. package/dist/electron-host.d.ts +7 -0
  13. package/dist/electron-host.js +54 -0
  14. package/dist/electron-native.d.ts +15 -0
  15. package/dist/electron-native.js +73 -0
  16. package/dist/electron.d.ts +4 -9
  17. package/dist/electron.js +85 -133
  18. package/dist/index.js +21 -0
  19. package/dist/native.d.ts +9 -24
  20. package/dist/native.js +48 -106
  21. package/dist/project-vite.d.ts +3 -0
  22. package/dist/project-vite.js +21 -0
  23. package/dist/resolve-tool-packages.d.ts +1 -0
  24. package/dist/resolve-tool-packages.js +29 -0
  25. package/dist/steam-upload.d.ts +27 -0
  26. package/dist/steam-upload.js +131 -0
  27. package/dist/steam.d.ts +22 -0
  28. package/dist/steam.js +193 -0
  29. package/dist/tool-packages.d.ts +15 -0
  30. package/dist/tool-packages.js +74 -0
  31. package/dist/web.js +10 -4
  32. package/dist/xbox.d.ts +1 -1
  33. package/dist/xbox.js +60 -57
  34. package/docs/capacitor.md +201 -0
  35. package/docs/native-bridge.md +147 -0
  36. package/docs/steam.md +384 -0
  37. package/docs/xbox.md +145 -0
  38. package/package.json +9 -12
  39. package/templates/electron/entitlements.plist +12 -0
  40. package/templates/electron/icons/icon.icns +0 -0
  41. package/templates/electron/icons/icon.ico +0 -0
  42. package/templates/electron/icons/icon.png +0 -0
  43. package/templates/electron/index.js +119 -0
  44. package/templates/electron/local-assets.cjs +24 -0
  45. package/templates/electron/native-bridge.cjs +98 -0
  46. package/templates/electron/package.json +11 -0
  47. package/templates/electron/preload.cjs +11 -0
  48. package/templates/xbox/src/platform/StbTextRasterizer.cpp +14 -10
  49. package/templates/xbox/tests/check_local.sh +12 -4
  50. package/templates/xbox/tests/stb_safety_test.cpp +126 -0
  51. package/templates/xbox/third_party/LICENSES.md +15 -0
  52. package/templates/xbox/third_party/stb_image.h +15 -10
  53. package/templates/xbox/third_party/stb_truetype.h +4 -4
  54. package/templates/xbox/third_party/stb_vorbis.c +1 -1
@@ -0,0 +1,73 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ export const validateNativeBridge = (value) => {
5
+ if (value === undefined) {
6
+ return;
7
+ }
8
+ if (value && value.prepare !== undefined && (typeof value.prepare !== "string" || !value.prepare.trim()
9
+ || path.isAbsolute(value.prepare) || value.prepare.includes("\0") || !/\.[cm]?js$/.test(value.prepare))) {
10
+ throw new Error("nativeBridge.prepare must be a project-relative Node.js script.");
11
+ }
12
+ if (!value || !Array.isArray(value.methods) || !value.methods.length
13
+ || value.methods.some((method) => typeof method !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.]{0,63}$/.test(method))
14
+ || !value.targets || typeof value.targets !== "object" || Array.isArray(value.targets)
15
+ || value.backgroundThrottling !== undefined && typeof value.backgroundThrottling !== "boolean") {
16
+ throw new Error("Invalid nativeBridge methods/targets.");
17
+ }
18
+ for (const [target, entry] of Object.entries(value.targets)) {
19
+ if (!/^(windows|linux)-(x64|arm64)$|^macos-(x64|arm64|universal)$/.test(target)
20
+ || !entry || typeof entry.directory !== "string" || !entry.directory.trim()
21
+ || typeof entry.executable !== "string" || !/^[a-zA-Z0-9_-][a-zA-Z0-9_.-]*$/.test(entry.executable)) {
22
+ throw new Error(`Invalid nativeBridge target: ${target}`);
23
+ }
24
+ }
25
+ };
26
+ export const stageNativeBridge = (root, host, config, target) => {
27
+ validateNativeBridge(config);
28
+ const entry = config.targets[target];
29
+ if (!entry) {
30
+ throw new Error(`nativeBridge requires a ${target} target (universal builds require universal native binaries).`);
31
+ }
32
+ const source = path.resolve(root, entry.directory);
33
+ const check = (file) => {
34
+ const stat = fs.lstatSync(file);
35
+ if (stat.isSymbolicLink() || !stat.isFile() && !stat.isDirectory()) {
36
+ throw new Error(`Native bridge bundles must contain only regular files/directories: ${file}`);
37
+ }
38
+ if (stat.isDirectory()) {
39
+ for (const child of fs.readdirSync(file)) {
40
+ check(path.join(file, child));
41
+ }
42
+ }
43
+ };
44
+ const destination = path.join(host, "native");
45
+ if (config.prepare) {
46
+ const script = fs.realpathSync(path.resolve(root, config.prepare));
47
+ const relative = path.relative(fs.realpathSync(root), script);
48
+ if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
49
+ throw new Error("nativeBridge.prepare must stay inside the project.");
50
+ }
51
+ fs.mkdirSync(destination, { "mode": 0o700 });
52
+ const result = spawnSync(process.execPath, [script, "--target", target, "--source", source, "--destination", destination], { "cwd": root, "stdio": "inherit", "shell": false, "timeout": 120000 });
53
+ if (result.error) {
54
+ throw result.error;
55
+ }
56
+ if (result.status !== 0) {
57
+ throw new Error(`Native preparation failed (${result.status}).`);
58
+ }
59
+ }
60
+ else {
61
+ check(source);
62
+ fs.cpSync(source, destination, { "recursive": true });
63
+ }
64
+ check(destination);
65
+ if (!fs.statSync(path.join(destination, entry.executable), { "throwIfNoEntry": false })?.isFile()) {
66
+ throw new Error(`Native bridge executable is missing: ${entry.executable}`);
67
+ }
68
+ if (!target.startsWith("windows-")) {
69
+ fs.chmodSync(path.join(destination, entry.executable), 0o755);
70
+ }
71
+ return { "executable": entry.executable, "methods": config.methods,
72
+ ...config.backgroundThrottling !== undefined ? { "backgroundThrottling": config.backgroundThrottling } : {} };
73
+ };
@@ -1,10 +1,5 @@
1
- /**
2
- * @description Electron ベースのデスクトップアプリ書き出し関数
3
- * (Windows / macOS / Linux、Steam・Epic Games Store 等の配布に共通)。
4
- * Export function for Electron-based desktop apps.
5
- *
6
- * @return {Promise}
7
- * @method
8
- * @public
9
- */
1
+ export declare const resolveElectronTarget: (platform: string, arch?: string, preview?: boolean) => {
2
+ platform: "win32" | "darwin" | "linux";
3
+ arch: "x64" | "arm64" | "universal";
4
+ };
10
5
  export declare const buildElectron: () => Promise<void>;
package/dist/electron.js CHANGED
@@ -1,142 +1,94 @@
1
- // Electron ベースのデスクトップ配布 (Steam / Epic Games Store など)。
2
- import pc from "picocolors";
3
- import fs from "fs";
4
- import cp from "child_process";
5
- import { api } from "@electron-forge/core";
1
+ // Electron desktop packages are unpacked application folders, ready for SteamPipe.
2
+ import pc from "./colors.js";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { createRequire } from "node:module";
6
+ import { pathToFileURL } from "node:url";
7
+ import { createElectronPackagerOptions, readElectronConfig } from "./electron-config.js";
6
8
  import { ctx } from "./context.js";
7
9
  import { $spawn } from "./utils.js";
8
- /**
9
- * @description electronがインストールされてなければインストールを実行
10
- * If electron is not installed, run install.
11
- *
12
- * @return {Promise}
13
- * @method
14
- * @private
15
- */
16
- const installElectron = () => {
17
- return new Promise((resolve, reject) => {
18
- if (fs.existsSync(`${process.cwd()}/electron/node_modules`)) {
19
- return resolve();
20
- }
21
- const stream = $spawn("npm", [
22
- "--prefix",
23
- `${process.cwd()}/electron`,
24
- "install",
25
- `${process.cwd()}/electron`
26
- ], { "stdio": "inherit" });
27
- stream.on("close", (code) => {
28
- if (code !== 0) {
29
- reject("`Electron` installation failed.");
30
- }
31
- console.log(pc.green("`Electron` successfully installed."));
32
- resolve();
33
- });
34
- });
35
- };
36
- /**
37
- * @description electronに含むリソースを初期化
38
- * Initialize resources included in electron
39
- *
40
- * @return {Promise}
41
- * @method
42
- * @private
43
- */
44
- const removeResources = () => {
45
- return new Promise((resolve, reject) => {
46
- const stream = cp.spawn("rm", [
47
- "-rf",
48
- `${process.cwd()}/electron/resources/`
49
- ], { "stdio": "inherit" });
50
- stream.on("close", (code) => {
51
- if (code !== 0) {
52
- reject("Failed to remove built resources.");
53
- }
54
- console.log(pc.green("Successfully remove built resources."));
55
- resolve();
56
- });
57
- });
10
+ import { getElectronVersion, withElectronHost, withElectronPackagerTemp } from "./electron-host.js";
11
+ import { findSteamLaunch, recordSteamPackage, writeSharedSteamDepots } from "./steam.js";
12
+ import { resolveToolPackages } from "./tool-packages.js";
13
+ export const resolveElectronTarget = (platform, arch = "", preview = false) => {
14
+ const os = platform.replace(/^steam:/, "");
15
+ const target = { "windows": "win32", "macos": "darwin", "linux": "linux" }[os];
16
+ if (target !== "win32" && target !== "darwin" && target !== "linux") {
17
+ throw new Error(`Unsupported Electron platform: ${platform}`);
18
+ }
19
+ const cpu = arch || (preview ? process.arch : target === "darwin" ? "universal" : "x64");
20
+ if (cpu !== "x64" && cpu !== "arm64" && cpu !== "universal") {
21
+ throw new Error(`Unsupported Electron architecture: ${cpu}`);
22
+ }
23
+ if (cpu === "universal" && target !== "darwin") {
24
+ throw new Error("--arch universal is only supported for macOS.");
25
+ }
26
+ if (preview && (target !== process.platform || cpu !== process.arch)) {
27
+ throw new Error("Electron preview must use the host OS and architecture.");
28
+ }
29
+ return { "platform": target, "arch": cpu };
58
30
  };
59
- /**
60
- * @description ビルドしたリソースをコピー
61
- * Copy built resources
62
- *
63
- * @return {Promise}
64
- * @method
65
- * @private
66
- */
67
- const copyResources = () => {
68
- return new Promise((resolve, reject) => {
69
- const stream = cp.spawn("cp", [
70
- "-r",
71
- `${ctx.buildDir}/`,
72
- `${process.cwd()}/electron/resources`
73
- ], { "stdio": "inherit" });
74
- stream.on("close", (code) => {
75
- if (code !== 0) {
76
- reject("Failed to copy built resources.");
77
- }
78
- console.log(pc.green("Successfully copy built resources."));
79
- resolve();
80
- });
31
+ const run = (command, args, cwd) => new Promise((resolve, reject) => {
32
+ const child = $spawn(command, args, { cwd, "stdio": "inherit" });
33
+ child.once("error", reject);
34
+ child.once("close", (code, signal) => {
35
+ if (code !== 0) {
36
+ reject(new Error(`${command} failed (${signal || code}).`));
37
+ return;
38
+ }
39
+ resolve();
81
40
  });
82
- };
83
- /**
84
- * @description Electron ベースのデスクトップアプリ書き出し関数
85
- * (Windows / macOS / Linux、Steam・Epic Games Store 等の配布に共通)。
86
- * Export function for Electron-based desktop apps.
87
- *
88
- * @return {Promise}
89
- * @method
90
- * @public
91
- */
41
+ });
92
42
  export const buildElectron = async () => {
93
- // reset
94
- await removeResources();
95
- // copy HTML, JavaScript
96
- await copyResources();
97
- if (ctx.preview) {
98
- $spawn("npx", [
99
- "electron",
100
- `${process.cwd()}/electron/index.js`
101
- ], { "stdio": "inherit" });
43
+ const root = process.cwd();
44
+ const config = readElectronConfig(root);
45
+ const os = ctx.platform.replace(/^steam:/, "");
46
+ const target = resolveElectronTarget(ctx.platform, ctx.arch || (ctx.preview ? "" : config.architectures[os]), ctx.preview);
47
+ if (target.arch === "universal" && process.platform !== "darwin") {
48
+ throw new Error("Build universal macOS applications on macOS.");
49
+ }
50
+ const steam = ctx.platform.startsWith("steam:");
51
+ const version = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8")).version;
52
+ const steamRoot = path.resolve(ctx.outDir, "steam");
53
+ const outDir = path.resolve(ctx.outDir, ctx.platformDir, "build", ctx.environment);
54
+ if (steam) {
55
+ // A failed rebuild must not leave a shared manifest pointing at partially replaced files.
56
+ fs.rmSync(path.join(outDir, "steam-package.json"), { "force": true });
57
+ writeSharedSteamDepots(steamRoot, ctx.environment, config, version);
102
58
  }
103
- else {
104
- await installElectron();
105
- console.log(pc.green("Start the `Electron` build process."));
106
- console.log();
107
- const packageOptions = {
108
- "dir": `${process.cwd()}/electron`,
109
- "outDir": `${ctx.outDir}/${ctx.platformDir}/build`,
110
- "platform": "",
111
- "arch": "all"
112
- };
113
- switch (ctx.platform) {
114
- case "windows":
115
- case "steam:windows":
116
- packageOptions.platform = "win32";
117
- break;
118
- case "macos":
119
- case "steam:macos":
120
- packageOptions.platform = "mas";
121
- break;
122
- case "linux":
123
- case "steam:linux":
124
- packageOptions.platform = "linux";
125
- break;
126
- default:
127
- console.log(pc.red("There is an error in the export platform settings."));
128
- console.log();
129
- process.exit(1);
59
+ const outputPaths = await withElectronHost(root, ctx.buildDir, config, async (dir) => {
60
+ const options = createElectronPackagerOptions(root, config, os, path.join(dir, "resources"));
61
+ if (steam && os === "macos" && !options.osxNotarize) {
62
+ console.log(pc.yellow("Local macOS build: unsigned/unnotarized builds must not be submitted for Steam release. See docs/steam.md."));
130
63
  }
131
- api
132
- .package(packageOptions)
133
- .then(() => {
134
- console.log(pc.green(`Finished building \`Electron\` for ${ctx.platform}.`));
135
- console.log();
136
- })
137
- .catch((error) => {
138
- console.log(pc.red("Export of Electron failed."));
139
- console.log(pc.red(error));
140
- });
64
+ console.log(pc.green(`Packaging Electron (${target.platform}/${target.arch})`));
65
+ const packages = await resolveToolPackages("electron");
66
+ const require = createRequire(packages["@electron/packager"]);
67
+ const { packager } = await import(pathToFileURL(require.resolve("@electron/packager")).href);
68
+ // The generated host has no npm dependencies or native addons. Packager downloads
69
+ // the pinned Electron runtime directly and uses its normal download cache.
70
+ return await withElectronPackagerTemp(async (tmpdir) => await packager({
71
+ ...options, dir, ...target,
72
+ tmpdir,
73
+ "out": outDir,
74
+ "electronVersion": getElectronVersion(),
75
+ "overwrite": true,
76
+ "prune": false
77
+ }));
78
+ }, `${os}-${target.arch}`);
79
+ if (steam) {
80
+ for (const content of outputPaths) {
81
+ recordSteamPackage(content, ctx.platform, target.arch, config, version);
82
+ }
83
+ writeSharedSteamDepots(steamRoot, ctx.environment, config, version);
84
+ }
85
+ console.log(pc.green(`Finished Electron export: ${outDir}`));
86
+ if (ctx.preview) {
87
+ const content = outputPaths[0];
88
+ const launch = findSteamLaunch(content, `steam:${os}`, config.executableName);
89
+ const executable = target.platform === "darwin"
90
+ ? path.join(content, launch, "Contents/MacOS", config.executableName)
91
+ : path.join(content, launch);
92
+ await run(executable, [], root);
141
93
  }
142
94
  };
package/dist/index.js CHANGED
@@ -8,6 +8,11 @@ import { loadConfig, buildWeb } from "./web.js";
8
8
  import { buildElectron } from "./electron.js";
9
9
  import { runNative, openNative, buildNative } from "./native.js";
10
10
  import { buildXbox } from "./xbox.js";
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import { readElectronConfig } from "./electron-config.js";
14
+ import { writeSharedSteamDepots } from "./steam.js";
15
+ import { uploadSteam } from "./steam-upload.js";
11
16
  // Node バージョン検証・引数解析・ctx 初期化 (不正時はここで終了)。
12
17
  initCli();
13
18
  /**
@@ -65,7 +70,23 @@ const multiBuild = async () => {
65
70
  */
66
71
  const execute = async () => {
67
72
  try {
73
+ if (ctx.steamUpload) {
74
+ await uploadSteam({
75
+ "root": process.cwd(), "steamRoot": ctx.steamRoot || "dist/steam", "environment": ctx.environment,
76
+ "branch": ctx.steamBranch || undefined, "comment": ctx.steamComment || undefined, "dryRun": ctx.dryRun
77
+ });
78
+ return;
79
+ }
68
80
  await loadConfig();
81
+ if (ctx.steamManifest) {
82
+ const config = readElectronConfig(process.cwd());
83
+ const version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
84
+ const pending = writeSharedSteamDepots(path.resolve(ctx.outDir, "steam"), ctx.environment, config, version);
85
+ if (pending.length) {
86
+ throw new Error(`Shared Steam depots are incomplete. Export or collect all required OS packages first.\n${pending.join("\n")}`);
87
+ }
88
+ return;
89
+ }
69
90
  await buildWeb();
70
91
  await multiBuild();
71
92
  }
package/dist/native.d.ts CHANGED
@@ -1,27 +1,12 @@
1
- /**
2
- * @description iOS用アプリの書き出し関数
3
- * Export function for iOS apps
4
- *
5
- * @return {Promise}
6
- * @method
7
- * @public
8
- */
1
+ /** Resolve from npm's persistent tool cache without installing into the game. */
2
+ export declare const getCapacitorCommand: () => Promise<{
3
+ cli: string;
4
+ env: NodeJS.ProcessEnv;
5
+ }>;
6
+ /** Await every CLI operation so failures propagate to the builder and CI. */
7
+ export declare const runCapacitor: (args: string[], root?: string) => Promise<void>;
8
+ /** Keep native customizations and update web assets/dependency paths in place. */
9
+ export declare const syncNative: () => Promise<void>;
9
10
  export declare const runNative: () => Promise<void>;
10
- /**
11
- * @description iOS/Androidアプリのオープン関数
12
- * Open function for iOS/Android apps
13
- *
14
- * @return {Promise}
15
- * @method
16
- * @public
17
- */
18
11
  export declare const openNative: () => Promise<void>;
19
- /**
20
- * @description iOS/Androidアプリのビルド関数
21
- * Build function for iOS/Android apps
22
- *
23
- * @return {Promise}
24
- * @method
25
- * @public
26
- */
27
12
  export declare const buildNative: () => Promise<void>;
package/dist/native.js CHANGED
@@ -1,123 +1,65 @@
1
- // iOS / Android (Capacitor) ビルド。
2
- import pc from "picocolors";
3
- import fs from "fs";
1
+ // iOS / Android (Capacitor) ビルド。SDKとCLIはnpxで必要時に取得する。
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
4
  import { ctx } from "./context.js";
5
5
  import { $spawn } from "./utils.js";
6
6
  import { CAPACITOR_CONFIG_NAME } from "./constants.js";
7
- /**
8
- * @description iOS/Androidのプロジェクトを生成
9
- * Generate iOS/Android project
10
- *
11
- * @return {Promise}
12
- * @method
13
- * @private
14
- */
15
- const generateNativeProject = () => {
7
+ import { resolveToolPackages } from "./tool-packages.js";
8
+ /** Resolve from npm's persistent tool cache without installing into the game. */
9
+ export const getCapacitorCommand = async () => {
10
+ const resolved = await resolveToolPackages("capacitor");
11
+ const packages = ["cli", "core", "ios", "android"].map((name) => resolved[`@capacitor/${name}`]);
12
+ // Keep the game's cwd for plugin discovery/hooks; supply SDKs through NODE_PATH.
13
+ const roots = [...new Set(packages.map((file) => path.dirname(path.dirname(path.dirname(file)))))];
14
+ return {
15
+ "cli": path.join(path.dirname(packages[0]), "bin", "capacitor"),
16
+ "env": { ...process.env, "NODE_PATH": [...roots, process.env.NODE_PATH].filter(Boolean).join(path.delimiter) }
17
+ };
18
+ };
19
+ /** Await every CLI operation so failures propagate to the builder and CI. */
20
+ export const runCapacitor = async (args, root = process.cwd()) => {
21
+ const { cli, env } = await getCapacitorCommand();
16
22
  return new Promise((resolve, reject) => {
17
- if (fs.existsSync(`${process.cwd()}/${ctx.platform}`)) {
18
- return resolve();
19
- }
20
- const stream = $spawn("npx", [
21
- "cap",
22
- "add",
23
- ctx.platform
24
- ], { "stdio": "inherit" });
25
- stream.on("close", (code) => {
23
+ const child = $spawn(process.execPath, [cli, ...args], { "cwd": root, env, "stdio": "inherit" });
24
+ child.once("error", reject);
25
+ child.once("close", (code, signal) => {
26
26
  if (code !== 0) {
27
- reject(`Failed generated ${ctx.platform} project.`);
27
+ reject(new Error(`Capacitor ${args.join(" ")} failed (${signal || code}).`));
28
+ return;
28
29
  }
29
- console.log(pc.green(`Successfully generated ${ctx.platform} project.`));
30
- console.log();
31
30
  resolve();
32
31
  });
33
32
  });
34
33
  };
35
- /**
36
- * @description Capacitor の webDir を現在のビルド出力へ書き換える
37
- * Point the Capacitor webDir at the current build output
38
- *
39
- * @return {void}
40
- * @method
41
- * @private
42
- */
43
- const applyCapacitorWebDir = () => {
44
- const config = JSON.parse(fs.readFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, { "encoding": "utf8" }));
45
- config.webDir = `${ctx.outDir}/${ctx.platformDir}/${ctx.environment}/`;
46
- fs.writeFileSync(`${process.cwd()}/${CAPACITOR_CONFIG_NAME}`, JSON.stringify(config, null, 2));
34
+ const prepareNativeProject = async () => {
35
+ if (ctx.platform !== "ios" && ctx.platform !== "android") {
36
+ throw new Error(`Unsupported Capacitor platform: ${ctx.platform}`);
37
+ }
38
+ const root = process.cwd();
39
+ const file = path.join(root, CAPACITOR_CONFIG_NAME);
40
+ const config = JSON.parse(fs.readFileSync(file, "utf8"));
41
+ // cap add performs an initial sync, so it must see the new webDir too.
42
+ config.webDir = path.relative(root, path.resolve(ctx.outDir, ctx.platformDir, ctx.environment)).replace(/\\/g, "/") + "/";
43
+ fs.writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
44
+ const platformDir = path.resolve(root, config[ctx.platform]?.path ?? ctx.platform);
45
+ if (!fs.existsSync(platformDir)) {
46
+ await runCapacitor(["add", ctx.platform]);
47
+ }
48
+ };
49
+ /** Keep native customizations and update web assets/dependency paths in place. */
50
+ export const syncNative = async () => {
51
+ await prepareNativeProject();
52
+ await runCapacitor(["sync", ctx.platform]);
47
53
  };
48
- /**
49
- * @description iOS用アプリの書き出し関数
50
- * Export function for iOS apps
51
- *
52
- * @return {Promise}
53
- * @method
54
- * @public
55
- */
56
54
  export const runNative = async () => {
57
- await generateNativeProject();
58
- // Capacitorの書き出しに必要な設定を生成
59
- applyCapacitorWebDir();
60
- $spawn("npx", [
61
- "cap",
62
- "run",
63
- ctx.platform
64
- ], { "stdio": "inherit" });
55
+ await prepareNativeProject();
56
+ await runCapacitor(["run", ctx.platform]);
65
57
  };
66
- /**
67
- * @description iOS/Androidアプリのオープン関数
68
- * Open function for iOS/Android apps
69
- *
70
- * @return {Promise}
71
- * @method
72
- * @public
73
- */
74
58
  export const openNative = async () => {
75
- await generateNativeProject();
76
- // Capacitorの書き出しに必要な設定を生成
77
- applyCapacitorWebDir();
78
- const stream = $spawn("npx", [
79
- "cap",
80
- "sync",
81
- ctx.platform
82
- ], { "stdio": "inherit" });
83
- stream.on("close", (code) => {
84
- if (code !== 0) {
85
- console.log(pc.red(`Failed to sync ${ctx.platform} project.`));
86
- return;
87
- }
88
- $spawn("npx", [
89
- "cap",
90
- "open",
91
- ctx.platform
92
- ], { "stdio": "inherit" });
93
- });
59
+ await syncNative();
60
+ await runCapacitor(["open", ctx.platform]);
94
61
  };
95
- /**
96
- * @description iOS/Androidアプリのビルド関数
97
- * Build function for iOS/Android apps
98
- *
99
- * @return {Promise}
100
- * @method
101
- * @public
102
- */
103
62
  export const buildNative = async () => {
104
- await generateNativeProject();
105
- // Capacitorの書き出しに必要な設定を生成
106
- applyCapacitorWebDir();
107
- const stream = $spawn("npx", [
108
- "cap",
109
- "sync",
110
- ctx.platform
111
- ], { "stdio": "inherit" });
112
- stream.on("close", (code) => {
113
- if (code !== 0) {
114
- console.log(pc.red(`Failed to sync ${ctx.platform} project.`));
115
- return;
116
- }
117
- $spawn("npx", [
118
- "cap",
119
- "build",
120
- ctx.platform
121
- ], { "stdio": "inherit" });
122
- });
63
+ await syncNative();
64
+ await runCapacitor(["build", ctx.platform]);
123
65
  };
@@ -0,0 +1,3 @@
1
+ import type * as Vite from "vite";
2
+ /** Resolve from the game, including when builder is launched through npx or a sibling checkout. */
3
+ export declare const loadProjectVite: (root?: string) => Promise<typeof Vite>;
@@ -0,0 +1,21 @@
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ /** Resolve from the game, including when builder is launched through npx or a sibling checkout. */
5
+ export const loadProjectVite = async (root = process.cwd()) => {
6
+ const require = createRequire(path.join(root, "package.json"));
7
+ let entry;
8
+ try {
9
+ entry = require.resolve("vite");
10
+ }
11
+ catch {
12
+ throw new Error(`Vite is not installed in ${root}. Install the game's dependencies with npm install first.`);
13
+ }
14
+ const imported = await import(pathToFileURL(entry).href);
15
+ // Vite 7's require condition resolves to its CJS API wrapper; Vite 8 is ESM.
16
+ const vite = imported.default ?? imported;
17
+ if (typeof vite.loadConfigFromFile !== "function") {
18
+ throw new Error(`The Vite installation in ${root} does not expose loadConfigFromFile.`);
19
+ }
20
+ return vite;
21
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ // Runs inside npx, where the downloaded tools' node_modules/.bin is on PATH.
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ const packages = JSON.parse(process.env.NEXT2D_TOOL_PACKAGES);
5
+ let resolved;
6
+ for (const bin of (process.env.PATH ?? "").split(path.delimiter)) {
7
+ if (path.basename(bin) !== ".bin") {
8
+ continue;
9
+ }
10
+ const files = {};
11
+ for (const [name, version] of Object.entries(packages)) {
12
+ const file = path.join(path.dirname(bin), name, "package.json");
13
+ if (fs.existsSync(file)) {
14
+ const pkg = JSON.parse(fs.readFileSync(file, "utf8"));
15
+ if (pkg.name === name && pkg.version === version) {
16
+ files[name] = file;
17
+ }
18
+ }
19
+ }
20
+ // All SDKs must come from the same installation, with the requested exact versions.
21
+ if (Object.keys(files).length === Object.keys(packages).length) {
22
+ resolved = files;
23
+ break;
24
+ }
25
+ }
26
+ if (!resolved) {
27
+ throw new Error("Could not resolve the requested build tools from npx's PATH.");
28
+ }
29
+ fs.writeFileSync(process.env.NEXT2D_TOOL_RESULT, JSON.stringify(resolved));
@@ -0,0 +1,27 @@
1
+ export interface ISteamUploadOptions {
2
+ root: string;
3
+ steamRoot: string;
4
+ environment: string;
5
+ branch?: string;
6
+ comment?: string;
7
+ dryRun?: boolean;
8
+ }
9
+ export declare const validateSteamBranch: (branch: unknown) => string;
10
+ /** Recreate manifests from validated package records, never from stale or edited VDF files. */
11
+ export declare const prepareSteamUpload: (options: ISteamUploadOptions) => {
12
+ appId: string;
13
+ branch: string;
14
+ version: string;
15
+ comment: string;
16
+ environment: string;
17
+ dryRun: boolean;
18
+ manifest: string;
19
+ launches: {
20
+ platform: string;
21
+ arch: string;
22
+ executable: string;
23
+ depotId: string;
24
+ }[];
25
+ };
26
+ /** Use SteamCMD's existing login session; passwords/Steam Guard codes never enter project config. */
27
+ export declare const uploadSteam: (options: ISteamUploadOptions) => Promise<void>;