@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.
- package/README.md +90 -57
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +68 -3
- package/dist/colors.d.ts +6 -0
- package/dist/colors.js +7 -0
- package/dist/context.d.ts +10 -1
- package/dist/context.js +7 -0
- package/dist/description.d.ts +2 -0
- package/dist/description.js +8 -0
- package/dist/electron-config.d.ts +62 -0
- package/dist/electron-config.js +134 -0
- package/dist/electron-host.d.ts +7 -0
- package/dist/electron-host.js +54 -0
- package/dist/electron-native.d.ts +15 -0
- package/dist/electron-native.js +73 -0
- package/dist/electron.d.ts +4 -9
- package/dist/electron.js +85 -133
- package/dist/index.js +21 -0
- package/dist/native.d.ts +9 -24
- package/dist/native.js +48 -106
- package/dist/project-vite.d.ts +3 -0
- package/dist/project-vite.js +21 -0
- package/dist/resolve-tool-packages.d.ts +1 -0
- package/dist/resolve-tool-packages.js +29 -0
- package/dist/steam-upload.d.ts +27 -0
- package/dist/steam-upload.js +131 -0
- package/dist/steam.d.ts +22 -0
- package/dist/steam.js +193 -0
- package/dist/tool-packages.d.ts +15 -0
- package/dist/tool-packages.js +74 -0
- package/dist/web.js +10 -4
- package/dist/xbox.d.ts +1 -1
- package/dist/xbox.js +60 -57
- package/docs/capacitor.md +201 -0
- package/docs/native-bridge.md +147 -0
- package/docs/steam.md +384 -0
- package/docs/xbox.md +145 -0
- package/package.json +9 -12
- package/templates/electron/entitlements.plist +12 -0
- package/templates/electron/icons/icon.icns +0 -0
- package/templates/electron/icons/icon.ico +0 -0
- package/templates/electron/icons/icon.png +0 -0
- package/templates/electron/index.js +119 -0
- package/templates/electron/local-assets.cjs +24 -0
- package/templates/electron/native-bridge.cjs +98 -0
- package/templates/electron/package.json +11 -0
- package/templates/electron/preload.cjs +11 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +14 -10
- package/templates/xbox/tests/check_local.sh +12 -4
- package/templates/xbox/tests/stb_safety_test.cpp +126 -0
- package/templates/xbox/third_party/LICENSES.md +15 -0
- package/templates/xbox/third_party/stb_image.h +15 -10
- package/templates/xbox/third_party/stb_truetype.h +4 -4
- 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
|
+
};
|
package/dist/electron.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
2
|
-
import pc from "
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
3
|
-
import
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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(`
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
fs.
|
|
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
|
|
58
|
-
|
|
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
|
|
76
|
-
|
|
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
|
|
105
|
-
|
|
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,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>;
|